java并发AQS原理之ReentrantLock

 

看本文建议结合源码

 

首先来看看ReentrantLock的构造方法,它的构造方法有两个,如所示:

public ReentrantLock() {

    sync = new NonfairSync();

}

public ReentrantLock(booleanfair) {

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

}

ReentrantLock中定义了三个内部类,分别是Sync以及Sync的子类FairSyncNonfairSync,分别实现了公平策略和非公平策略,默认实现的是非公平策略。

 

公平和非公平的第一个区分点,首先来看NonfairSynclock方法

finalvoid lock() {

    if (compareAndSetState(0, 1))

         setExclusiveOwnerThread(Thread.currentThread());

    else

         acquire(1);

}

假设有三个并发的线程,第一个线程调用lock的时候,compareAndSetState(0,1)是可以正常执行的返回true,然后把当前线程置为独占模式的当前所有者,然后该线程可以执行后续操作。如果第二个线程被放到队列中,第三个线程走到compareAndSetState(0,1),第一个线程刚好unlock掉,那么第三个线程就会直接执行,而不是等待第二个线程执行后,这也就是非公平模式的特点。第二个线程调用lock的时候,compareAndSetState(0,1)返回false,执行acquire(1),该方法会调用tryAcquire(1)acquireQueued(addWaiter(Node.EXCLUSIVE), 1)

先来看下tryAcquire(1),该方法的实现是nonfairTryAcquire方法,代码如下:

finalboolean nonfairTryAcquire(intacquires) {

    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()方法的后续操作。

猜你喜欢

转载自nameethan.iteye.com/blog/2420241