ReentrantLock of java concurrent AQS principle

 

See this article to suggest combining the source code

 

First, let's take a look at the construction method of ReentrantLock . It has two construction methods , as shown:

public ReentrantLock() {

    sync = new NonfairSync();

}

publicReentrantLock(booleanfair) { 

    sync = fair ? new FairSync() : new NonfairSync();

}

Three inner classes are defined in ReentrantLock , namely Sync and the subclasses of Sync , FairSync and NonfairSync , respectively, which implement fair policy and unfair policy, and the default implementation is non-fair policy.

 

The first point of distinction between fairness and unfairness, let's first look at the lock method of NonfairSync

final void lock () {

    if (compareAndSetState(0, 1))

         setExclusiveOwnerThread(Thread.currentThread());

    else

         acquire(1);

}

Assuming that there are three concurrent threads, when the first thread calls lock , compareAndSetState(0,1) can be executed normally and returns true , and then the current thread is set as the current owner of the exclusive mode , and then the thread can perform subsequent operations . If the second thread is put in the queue, the third thread goes to compareAndSetState(0,1) , and the first thread just unlocks , then the third thread will execute directly instead of waiting for the second thread to execute Finally, this is the characteristic of the unfair mode. When the second thread calls lock , compareAndSetState(0,1) returns false and executes acquire(1) , which calls tryAcquire(1) and acquireQueued(addWaiter(Node.EXCLUSIVE), 1)

Let's take a look at tryAcquire(1) first . The implementation of this method is the nonfairTryAcquire method . The code is as follows:

final boolean nonfairTryAcquire ( int acquires ) {

    final Thread current = Thread.currentThread();

    intc = getState();

    if (c == 0) {

        if (compareAndSetState(0, acquires)) {

            setExclusiveOwnerThread(current);

            returntrue;

        }

    }

    elseif (current == getExclusiveOwnerThread()) {

        intnextc = c + acquires;

        if (nextc < 0) // overflow

            thrownew Error("Maximum lock count exceeded");

        setState(nextc);

        returntrue;

    }

    returnfalse;

}

看代码,结合三个线程并发的场景,第一个线程lock后,会把state(默认值是0)的值置为1,所以c的值现在是1getExclusiveOwnerThread()是第一个线程,所以直接返回false,执行acquireQueued(addWaiter(Node.EXCLUSIVE), 1),方法addWaiter(Node.EXCLUSIVE)的作用是根据给的排他模式把当前线程(也就是第二个线程)加入队列中,此时的队列情况如下:

 

首次有线程(Node)进入队列时,会先new一个Node对象作为头部,然后把首次进队列的线程(Node)挂在其后面。Node对象是包含当前线程和模型的作为队列元素

然后执行acquireQueued(final Node node, int arg),来看下代码:

finalboolean acquireQueued(final Node node, intarg) {

    booleanfailed = true;

    try {

        booleaninterrupted = false;

        for (;;) {

            final Node p = node.predecessor();

            if (p == head && tryAcquire(arg)) {

                setHead(node);

                p.next = null; // help GC

                failed = false;

                returninterrupted;

            }

            if (shouldParkAfterFailedAcquire(p, node) &&

                parkAndCheckInterrupt())

                interrupted = true;

        }

    } finally {

        if (failed)

            cancelAcquire(node);

    }

}

第二个线程入队列,前面是一个初始化的对象也是head的值,进入循环后,会再次tryAcquire(1),假设依然获取不到,则进入第二个if语句中,执行shouldParkAfterFailedAcquire(p, node)方法和parkAndCheckInterrupt()方法,parkAndCheckInterrupt()就会把第二个线程阻塞起来。

 

公平和非公平的第二个区分点在tryAcquire(int acquires)方法中,如下:

protectedfinalboolean tryAcquire(intacquires) {

    final Thread current = Thread.currentThread();

    intc = getState();

    if (c == 0) {

        if (!hasQueuedPredecessors() &&

            compareAndSetState(0, acquires)) {

            setExclusiveOwnerThread(current);

            returntrue;

        }

    }

    elseif (current == getExclusiveOwnerThread()) {

        intnextc = c + acquires;

        if (nextc < 0)

            thrownew Error("Maximum lock count exceeded");

        setState(nextc);

        returntrue;

    }

    returnfalse;

}

当第一个线程走完,第二个线程在队列中,第三个线程调用acquire(int arg)时先调用tryAcquire方法,会进行!hasQueuedPredecessors() && compareAndSetState(0, acquires)判断,而非公平的只有进行了compareAndSetState(0, acquires)判断,不考虑队列是否有等待的线程,而hasQueuedPredecessors()该方法就是判断队列中是否有阻塞的线程,如果有返回true,然后执行acquireQueued方法入队列。在第三个线程入队列同时,第一个线程走完调用unlock操作,该操作就会去调用release方法,如下

publicfinalboolean release(intarg) {

    if (tryRelease(arg)) {

        Node h = head;

        if (h != null && h.waitStatus != 0)

            unparkSuccessor(h);

        returntrue;

    }

    returnfalse;

}

最终调用unparkSuccessor(h)方法,

privatevoid unparkSuccessor(Node node) {

    intws = node.waitStatus;

    if (ws < 0)

        compareAndSetWaitStatus(node, ws, 0);

    Node s = node.next;

    if (s == null || s.waitStatus > 0) {

        s = null;

        for (Node t = tail; t != null && t != node; t = t.prev)

            if (t.waitStatus <= 0)

                s = t;

    }

    if (s != null)

        LockSupport.unpark(s.thread);

}

 

从队列中获取第二个线程,unpark操作。阻塞结束后还在acquireQueued(final Node node, intarg)的循环体中,此时!hasQueuedPredecessors()compareAndSetState(0, acquires)都为true,并把当前线程置为独占模式的当前所有者,然后该线程可以执行程序中lock()方法的后续操作。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326027502&siteId=291194637