Reentrant lock ReentrantLock source code reading

Reentrant lock ReentrantLock, as the name implies, is to support the re-entry of the lock, which indicates that the lock is capable of supporting a thread locking duplication of resources. In addition, the lock also support access to fair and unfair selection at lock.

Read this before reentrant lock class, you can read my previous two articles, there is a basic understanding of the lock and AbstractQueuedSynchronizer these two classes of functions and design. And then looked at a class, it will be better understood.

AQS (AbstractQueuedSynchronizer) Source queue synchronizer reading (a) www.jianshu.com/p/e0066f934... AQS (AbstractQueuedSynchronizer) Source queue read synchronizer (b) www.jianshu.com/p/a41088fc1...

Then we can know in general, ReentrantLock is a reentrant lock, it will be realized Lock this class and override a few methods provided by the class: // acquire the lock, the lock is released void lock (); void unlock () ; // can interrupt the corresponding thread to acquire the lock, when the thread is interrupted, the lock will be released. And general lock will not respond to void lockInterruptibly () throws InterruptedException; // attempt to acquire the lock, get less false boolean tryLock return (); // Like a boolean tryLock (long time, TimeUnit unit) throws InterruptedException; // get the corresponding condition, will specialize in the back, now just know that this can be used with the completion of different threads waiting / notification mechanism. Condition newCondition ();

Then the above general rewritten several methods, you rely queue synchronizer acquires synchronization state, need not obtain the synchronous queue and the like were added, it is generally also design tools and then to rewrite the Sync inherited AbstractQueuedSynchronizer accordance with various lock AQS Some methods provided.

After inheriting AQS subclass override method above AQS provides external ReentrantLock is to achieve several methods are generally provided Lock Sync inherited AQS call inside the method. For example: when locked:

//调用的是AQS的子类sync的lock
public void lock() {
        sync.lock();
    }
//而lock方法一般会根据具体的锁的设计去实现我们已经重写的acquire方法。大体的锁的设计不会偏差太多。
复制代码

Then we specifically look at: ReentrantLock is a reentrant lock. About thread scheduling policy into fair scheduling and unfair scheduling. His design also has an internal static inner classes inherit Sync AQS, and in order to distinguish between different scheduling strategies, there are designed two subclasses inherit Sync,

static final class NonfairSync extends Sync{.....}
static final class FairSync extends Sync{.....}
复制代码

See the class name can be seen, one for non-fair scheduling policy designed to lock a fair scheduling. Two different implementations rewritable's synchronous queue.

Look constructor:

public ReentrantLock() {
        sync = new NonfairSync();
    }

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

This time should not have to explain it, see the class name. So reentrant lock is a non-default fair scheduling policy, that thread execution order is not executed in the order of execution time, non-fair, more efficient.

Reentrancy

Reentrant lock after the name suggests is a thread to acquire the lock, the lock can be acquired again, without being blocked by locks. The lock realized the need to address two issues:

1. thread acquires the lock again. Lock need to identify whether the thread to acquire the lock for the current thread lock occupied, and if so, successfully acquired again.

2. The final release of the lock. . Thread repeated n times to obtain the lock, then the n-th release the lock, other threads can obtain the lock.

When the locks for non-default fair scheduling to get view synchronization status, how to deal with:

 final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //获取当前state,重入数量
            int c = getState();
            //如果还未有线程获取该锁
            if (c == 0) {
            //获取同步状态成功,则设置当前线程为持有锁的线程
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如果已有线程获取该锁,且该线程为已持有锁的线程
            else if (current == getExclusiveOwnerThread()) {
              //设置重入的数量。并返回获取锁成功
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
复制代码

This method increases the processing logic acquires synchronization status again: by determining whether the current thread acquires the lock for the thread to determine whether the operation is successful acquisition, if the thread request again to acquire the lock, then the sync state increase value and returns true, indicating acquiring synchronization status successfully.

Successfully acquired lock thread gets the lock again, only increased the value of the synchronization status, which also requires ReentrantLock reduce synchronization state value at the time of the release of sync.

protected final boolean tryRelease(int releases) {
//释放时减去同步状态值
            int c = getState() - releases;
            //如果当前线程与持有线程不一致,则报对象头状态异常
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
          //如果同步状态值为0,代表该锁已全部释放,需要释放锁,使其他线程能够获取该锁
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
复制代码

If the lock is acquired n times, then the first (n-1) times tryRelease (int releases) method must return false, but only the release of the complete synchronization status can return true. It can be seen that the method will be synchronized state is zero as a condition for eventual release, when the synchronization status 0, will occupy a thread set to null, and returns true, indicating a successful release.

### for non fair locks, as long as the CAS set up synchronization status is successful, the current thread acquired the lock, the lock is different and fair.

protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
//主要区别如下: 即加入了同步队列中当前节点是否有前驱节点的判断,如果该方法返回true,
//则表示有线程比当前线程更早地请求获取锁,因此需要等待前驱线程获取并释放锁之后才能继续获取锁。
                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;
复制代码

Non-locking fair scheduling policy process

final void lock() {

//1.如果同步操作state,获取同步状态成功,则设置当前线程为当前独占式获取锁.否则进行获取。

//compareAndSetState(0, 1)  使用的是:
//unsafe.compareAndSwapInt(this, stateOffset, expect, update);  CAS偏移地址来直接修改state的值。
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                // 2.否则获取同步状态。进去里面看实现
               acquire(1);
        }

//1.产时获取同步状态,(tryAcquire),  
失败则加入队尾tail,状态设置为EXCLUSIVE,(addWaiter(Node.EXCLUSIVE), arg))
同时进入自旋去获取同步状态,直到该节点前一个节点为头节点并获取成功,则出队列,并唤醒下一个节点,并且响应中断。(acquireQueued()
public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

tryAcquire()看上面的分析.主要调用我们NonfairSync重写的nonfairTryAcquire。
如果获取非公平的可重入锁失败,则执行下面方法。该方法不做具体分析了,之前已经有具体分析过了。
final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                // 1. 获得当前节点的先驱节点
                final Node p = node.predecessor();
                // 2. 当前节点能否获取独占式锁                  
                // 2.1 如果当前节点的先驱节点是头结点并且成功获取同步状态,即可以获得独占式锁
                if (p == head && tryAcquire(arg)) {
                    //队列头指针用指向当前节点
                    setHead(node);
                    //释放前驱节点
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                // 2.2 获取锁失败,线程进入等待状态等待获取独占式锁
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
}
复制代码

Unfair scheduling policy to release the lock of the process is not to say, above.

Then would have wanted to share with you again, fair scheduling acquire the lock and unlock the process, in fact, not. Unlocking process is the same, the front said. Then locked the difference is only a little difference. Is to get synchronized when the state needs to determine whether the front head node, if not you can get synchronized state directly, or continue to spin. Because fair scheduling is performed according to a time order of the time of the thread to acquire the lock, by controlling this point, it determines whether a time for obtaining synchronization state, thereby controlling the order to acquire a lock.

### from above the line marking the start of view, the following code logic nonfairTryAcquire been substantially, the only difference is the addition of logic judgment hasQueuedPredecessors, which may know the name of the method used to determine whether the current node in the synchronization queue there predecessor node to determine if there is a precursor node illustrate the request thread resources than the current thread earlier, according to the fairness of the current thread resource request fails. If the current node without predecessor node then determines the necessity of logic behind only then do. Every fair locks are acquired from the synchronization queue to lock the first node, rather than the fairness of the lock is not necessarily, it may just be able to get a thread releases the lock again to lock.

Fair and unfair lock lock VS

1. fair locks to lock every acquisition synchronization queue first node, the absolute guarantee request time order on the resource, rather than a fair lock may have just released the next thread lock to continue to acquire the lock, it may lead to other threads can never get to the lock, causing "hunger" phenomenon.

2. In order to guarantee a fair lock on the order of time, need frequent context switching, rather than a fair lock will reduce certain context switching, reducing performance overhead. Thus, non-default selection of ReentrantLock fair lock, in order to reduce part of the context switch to ensure a greater throughput of the system.

Writing is not easy, give praise

Guess you like

Origin juejin.im/post/5d67e67c51882508a225f57d