1. JUC source Series Analysis reentrant lock --ReentrantLock

JUC (java.util.concurrent) package occupying the absolute position in concurrent programming in JAVA, can see its shadow in the major open-source framework. JUC recently read the source code, make a continuous output, the analysis tool from concurrent concurrent container and then thread pool. While browsing, I suggest you open the corresponding source code, follow this paper together.

Opening question:

  1. Why a .lock () method can be locked for a synchronized block? (This article core)
  2. Re locked into is how to achieve?
  3. Where is the difference between fair and unfair lock lock in?
  4. And acquiring the lock timeout interrupt is how to achieve?

Lock implementation process

First look at several methods of ReentrantLock:

private final Sync sync;
public ReentrantLock() {sync = new NonfairSync();}
public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}
public void lock() {sync.lock();}
public void unlock() {sync.release(1);}
复制代码

We can find ReentrantLock of locking, unlocking methods are by calling its internal implementation Sync, Sync and its interior is an abstract class that implements respectively FairSync and NonfairSync, represent the realization of fair and unfair lock lock. Using non-default fair locks. We look at the Sync class diagram. Above the parent class is the famous AQS, I will analyze in the next article.

Then we take a look at FairSync locked the whole process is kind of how.

FairSync # lock() :
final void lock() {
    acquire(1);
}
// 发现其调用的是 AQS 中的 acquire 方法,深入看一下
AbstractQueuedSynchronizer # acquire()
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
复制代码

We can find acquire () where a determination is divided into two steps, roughly, which means that the first attempt can not get into the lock, if not, then enters a thread to wait in line to , respectively is tryAcquire (arg) and acquireQueued (addWaiter (~)), acquireQueued this method is to achieve AQS queuing, I will put # in speaking, so we here can be understood as first with tryAcquire () method can not get to try the lock, get time will return true, this method will return directly. If you can not get a lock, and went to the AQS waiting in line to go.

We focus here look at the implementation tryAcquire (), we will find that this is a template provided by AQS method, we can find in its implementation in FairSync:

FairSync # tryAcquire() : 
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    // 获取 AQS 中的 state 字段,在 ReentrantLock 中,state == 0 代表锁处于空闲状态,state > 0 代表锁已经被某个线程获取了
    int c = getState();
    if (c == 0) {
        // 首先判断 AQS 的是否有其他线程在排队,对于公平锁来说是要讲究先来后到的
        if (!hasQueuedPredecessors() &&
            // 使用 CAS 来设置 state 
            compareAndSetState(0, acquires)) {
            // 设置当前线程为锁的持有者
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 判断当前线程是否为锁的持有线程,如果是的话代表重入了
    else if (current == getExclusiveOwnerThread()) {
        // 从这里可以看出 state 代表了锁重入的次数
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        // 由于 state 是大于 1 的,其他线程是没法到达这一步的,所以不需要 CAS 就可以直接设置 state
        setState(nextc);
        return true;
    }
    return false;
}
复制代码

Here we analyze a little CAS. compareAndSetState(0, acquires)This code uses a Unsafeclass of variables AQS statemodified, this modification occurs directly in the local memory on. First Unsafelooks to stateposition in local memory, it do comparison with 0, equal, then the update. There seems to be two steps (first comparative updated), the bottom is actually completed by a CPU command, so the operation is atomic. And it would have locked the bus before executing this command, so that other processor is temporarily unable to access memory. This way ensures for statesecurity updates.

From the perspective of multithreaded concurrent analysis of how to prevent this code, we can find the thread you want to get a lock, this function returns true, it is to go through compareAndSetState(0, acquires)this CAS operation. When a thread to get the lock, then other threads when the CAS will find on the local memory stateis not 0, so returns failure. And CAS and ensures the atomicity, we can be regarded as among the various threads compareAndSetState(0, acquires)are executed sequentially, the situation is complicated by the absence.

Watching the process of locking, unlocking let's look at how to achieve.

ReentrantLock # unlock
public void unlock() {
    // 直接调用了 AQS 的 release
    sync.release(1);
}
AbstractQueuedSynchronizer # release
public final boolean release(int arg) {
    // 解锁的模板方法,在ReentrantLock的内部类实现
    if (tryRelease(arg)) {
        // 留到下节分析,实现了唤醒队列中排着队的其他线程
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
ReentrantLock # Sync # tryRelease()
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;
}
复制代码

To achieve the release of the lock is relatively simple and straightforward update stateand exclusiveOwnerThreaddoes not need to be considered concurrently, because before these two updates, other threads of the stateupdate will fail. At this point of the ReentrantLockanalysis is to unlock the lock on the end, in fact, read the source code will find ReentrantLockthat by CAS operations on one variable, success is to get locked up, went to line unsuccessful. As to how to line up, AQS is all concurrent functional tools to offer.

Answers opening

Reentrant lock is how to achieve

In fact, by just analyzing the locking procedure we can already get a reentrant lock when the lock is held by a thread when the object is present, it is for stateaccumulation. However, because of the unlocking operation only stateminus one, so when using a reentrant lock or unlock operation remember to call the corresponding number of times.

Where is the difference between fair and unfair lock lock in

Just now we are to get FairSyncthat is fair lock analysis, I look at the door and then NonfairSyncthe lock is how conducted.

NonfairSync#lock()

final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}
复制代码

We can find unfair competition before the lock is locked to try a CAS operation (that is, the feeling of jump the queue), the other is in the tryAcquiremiddle, to determine whether there is a column queued threads removed. So even if there are multiple queues line up in front of this thread, the thread also have the opportunity to get the first lock.

And acquiring the lock timeout interrupt is how to achieve?

ReentrantLockProvides a way to get the lock by specifying a time for us, when the time is up and still did not get the lock, then it will return a false.

ReentrantLock#tryLock
public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
Sync#tryAcquireNanos
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    // 尝试获取锁,如果不成功的话就调用 AQS 支持的时间排队法了
    return tryAcquire(arg) ||
        doAcquireNanos(arg, nanosTimeout);
}

复制代码

We can find support timeout mechanism mainly by AQS of doAcquireNanos(arg, nanosTimeout)implementation. This method of analysis which I will in the next one, here we just need to know that it is the same thread into the queue to stand in line when it reaches the timeout, it returns a false.

to sum up

Today, we analyzed the ReentrantLocksource code to know the lock unlock process is actually in competition stateline up the ability to modify variables, for there is no competition to lock the thread is provided by AQS queue waiting to enter. In addition ReentrantLockalso provides a method get its related variables, such as access to the thread holding the lock, get statethe value, get the whole line up queues, we recommend that you view the source code of these methods.

Guess you like

Origin juejin.im/post/5d8d6c2ce51d4578014332cd