ReentrantLock in Java

table of Contents

1. ReentrantLock initialization

2. ReentrantLock internal structure

3. Common methods of ReentrantLock

4. Nonfair lock

4.1 Principle of lock () method

4.2. Principle of TryAcquire () method

5. Fairlock FairSync

5.1 Principle of lock () and TryAcquire () methods

5.2 The principle of hasQueuedPredecessors () method

6. tryLock method

6.1 tryLock()

6.2 tryLock(long timeout, TimeUnit unit)

7. Use of ReentrantLock

8. Summary


ReentrantLock is reentrant the exclusive lock . According to the parameters, it can be determined whether it is a fair lock or an unfair lock .

 

1.  ReentrantLock initialization

// 非公平锁
ReentrantLock lock = new ReentrantLock();
// 公平锁
ReentrantLock lock = new ReentrantLock(true);

ReentrantLock defaults to an unfair lock, and you can pass in the parameter true to obtain a fair lock. The subclasses FairSync and NonfairSync implement lock fair and unfair strategies respectively. ReentrantLock initialization source code is as follows:

public ReentrantLock() {
    sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

 

2. ReentrantLock internal structure

  • ReentrantLock implements the Lock interface
    • Internal subclass Sync inherits AbstractQueuedSynchronizer abstract class
    • The internal subclasses FairSync and NonfairSync inherit the Sync class

 

3. Common methods of ReentrantLock

  • lock () lock method
    • Execution logic: lock ()-> acquire ()-> tryAcquire ()
  • unlock () unlock method
  • tryLock (), tryLock (long timeout, TimeUnit unit) try to acquire the lock

 

4.   Nonfair lock 

4.1 Principle of lock () method

i. Set state from 0 to 1 based on CAS

ii. If the setting is successful, set the exclusive lock thread as the current thread

iii. After the request fails, try again

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;
    final void lock() {
        // 基于 CAS 尝试将 state 从 0 设置为 1
        if (compareAndSetState(0, 1))
            // 独享锁的线程设置为当前线程
            setExclusiveOwnerThread(Thread.currentThread());
        else
            // 重试一次(内部调用 tryAcquire 方法)
            acquire(1);
    }
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

The acquire method in AQS executed after acquiring the lock fails as follows.

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

Since AQS does not provide the available tryAcquire method, the tryAcquire method needs to be customized by the subclass, so here tryAcquire will call the tryAcquire method implemented in NonfairSync.

4.2. Principle of TryAcquire () method

The nonfairTryAcquire method is called in the tryAcquire method of NonfairSync

The source code of nonfairTryAcquire is as follows:

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    // 判断 state 是否为 0
    if (c == 0) {
        // 尝试 state 的值从 0 改为 1
        if (compareAndSetState(0, acquires)) {
            // 设置获取锁的线程为当前线程
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) { // 如果持有锁的线程为当前线程
        // state 的值 + acquire
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        // 设置 state 值为新值
        setState(nextc);
        return true;
    }
    return false;
}

i. Determine if state is 0, if it is 0, try to acquire the lock

ii. Not 0, determine whether the thread holding the lock is the current thread

iii. If it is the current thread, reassign state + acquire to state (so ReentrantLock is a reentrant lock)

iv. The thread holding the lock is not the current thread, false is returned

 

5.   fair lock  FairSync

5.1  Principle of lock () and TryAcquire () methods

The principles of fair locks and unfair locks are similar, the difference is that the tryAcquire method has more hasQueuedPredecessors judgment:

static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;
    final void lock() {
        acquire(1);
    }
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            // 公平锁比非公平锁多了 hasQueuedPredecessors 的判断
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
}

5.2 The principle of hasQueuedPredecessors () method

public final boolean hasQueuedPredecessors() {
    Node t = tail;
    Node h = head;
    Node s;
    return h != t &&
              (
                (s = h.next) == null || 
                s.thread != Thread.currentThread()
            );
}

i. If the head node and the tail node are not equal (h! = t), it means that more than one thread is requesting the lock

ii. Determine whether head.next is null, if it is null, return true

h! = t at the same time as h.next == null: when other threads are enqueuing for the first time, compareAndSetHead (node) is completed, and tail = head statement has not been executed, then tail = null, head = newNode , head.next = null

iii. s.thread! = Thread.currentThread () to determine whether the current thread is the second thread

The judgment of hasQueuedPredecessors guarantees the sequence of threads acquiring locks, so FairSync implements fair locks.

 

6. tryLock method

6.1 tryLock()

Attempt to acquire the lock, true if the lock is acquired, false if the acquisition fails

public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}

The nonfairTryAcquire called internally by tryLock is consistent with the unfair lock code, so tryLock () uses an unfair strategy.

6.2 tryLock(long timeout, TimeUnit unit)

The difference with tryLock (): This method can set the timeout time, and returns false if the lock is not obtained within the timeout time.

 

7. Use of ReentrantLock

lock.lock();
try {
    //TODO
} finally {
    lock.unlock();
}

 

8. Summary

i. The underlying layer of ReentrantLock is a reentrant exclusive lock implemented using AQS

ii. ReentrantLock has both fair and unfair implementation, the default is unfair

iii. For the state in AQS, lock state + 1, unlock state-1, so you need to unlock a few times

iv. state is 0 means the current lock is idle, greater than or equal to 1, indicating that the lock has been occupied

Published 7 original articles · won 3 · views 309

Guess you like

Origin blog.csdn.net/sl285720967/article/details/105547762
Recommended