欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java并发AQS原理之ReentrantLock

程序员文章站 2022-04-19 08:23:10
...

 

看本文建议结合源码

 

首先来看看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)的作用是根据给的排他模式把当前线程(也就是第二个线程)加入队列中,此时的队列情况如下:

 
java并发AQS原理之ReentrantLock
            
    
    博客分类: java ReentrantLockAQS并发锁

首次有线程(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()方法的后续操作。

  • java并发AQS原理之ReentrantLock
            
    
    博客分类: java ReentrantLockAQS并发锁
  • 大小: 1.6 KB