魔鬼在细节,明白Java并发底层之AQS完成

魔鬼在细节,明白Java并发底层之AQS完成

jdk的JUC包(java.util.concurrent)供应大批Java并发东西供应运用,基础由Doug Lea编写,许多处所值得进修和自创,是进阶晋级必经之路

本文从JUC包中经常使用的对象锁、并发东西的运用和功用特征入手,带着题目,由浅到深,一步步理会并发底层AQS笼统类详细完成

名词解释

1 AQS

AQS是一个笼统类,类全途径java.util.concurrent.locks.AbstractQueuedSynchronizer,笼统行列同步器,是基于模板形式开辟的并发东西笼统类,有以下并发类基于AQS完成:

2 CAS

CAS是Conmpare And Swap(比较和交流)的缩写,是一个原子操纵指令

CAS机制当中运用了3个基础操纵数:内存地址addr,预期旧的值oldVal,要修正的新值newVal
更新一个变量的时刻,只要当变量的预期值oldVal和内存地址addr当中的现实值相同时,才会将内存地址addr对应的值修正为newVal

基于乐观锁的思绪,经由历程CAS再不停尝试和比较,能够对变量值线程平安地更新

3 线程中缀

线程中缀是一种线程合作机制,用于合作其他线程中缀使命的实行

当线程处于壅塞守候状况,比方挪用了wait()、join()、sleep()要领以后,挪用线程的interrupt()要领以后,线程会立时退出壅塞并收到InterruptedException;

当线程处于运转状况,挪用线程的interrupt()要领以后,线程并不会立时中缀实行,须要在线程的详细使命实行逻辑中经由历程挪用isInterrupted() 要领检测线程中缀标志位,然后主动相应中缀,通常是抛出InterruptedException

对象锁特征

下面先引见对象锁、并发东西有哪些基础特征,背面再逐渐睁开这些特征怎样完成

1 显式猎取

以ReentrantLock锁为例,重要支撑以下4种体式格局显式猎取锁

  • (1) 壅塞守候猎取
ReentrantLock lock = new ReentrantLock();
// 一向壅塞守候,直到猎取胜利
lock.lock();
  • (2) 无壅塞尝试猎取
ReentrantLock lock = new ReentrantLock();
// 尝试猎取锁,假如锁已被其他线程占用,则不壅塞守候直接返回false
// 返回true - 锁是余暇的且被本线程猎取,或许已被本线程持有
// 返回false - 猎取锁失利
boolean isGetLock = lock.tryLock();
  • (3) 指定时候内壅塞守候猎取
ReentrantLock lock = new ReentrantLock();
try {
    // 尝试在指定时候内猎取锁
    // 返回true - 锁是余暇的且被本线程猎取,或许已被本线程持有
    // 返回false - 指定时候内未猎取到锁
    lock.tryLock(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
    // 内部挪用isInterrupted() 要领检测线程中缀标志位,主动相应中缀
    e.printStackTrace();
}
  • (4) 相应中缀猎取
ReentrantLock lock = new ReentrantLock();
try {
    // 相应中缀猎取锁
    // 假如挪用线程的thread.interrupt()要领设置线程中缀,线程退出壅塞守候并抛出中缀非常
    lock.lockInterruptibly();
} catch (InterruptedException e) {
    e.printStackTrace();
}

2 显式开释

ReentrantLock lock = new ReentrantLock();
lock.lock();
// ... 种种营业操纵
// 显式开释锁
lock.unlock();

3 可重入

已猎取到锁的线程,再次要求该锁能够直接取得

4 可同享

指同一个资本许可多个线程同享,比方读写锁的读锁许可多个线程同享,同享锁能够让多个线程并发平安地接见数据,进步顺序执效力

5 平正、非平正

平正锁:多个线程采纳先到先得的平正体式格局合作锁。每次加锁前都邑搜检守候行列内里有无线程列队,没有才会尝试猎取锁。
非平正锁:当一个线程采纳非平正的体式格局猎取锁时,该线程会首先去尝试猎取锁而不是守候。假如没有猎取胜利,才会进入守候行列

由于非平正锁体式格局能够使厥后的线程有肯定概率直接猎取锁,减少了线程挂起守候的概率,机能优于平正锁

AQS完成道理

1 基础概念

(1) Condition接口

相似Object的wait()、wait(long timeout)、notify()以及notifyAll()的要领连系synchronized内置锁能够完成能够完成守候/关照形式,完成Lock接口的ReentrantLock、ReentrantReadWriteLock等对象锁也有相似功用:

Condition接口定义了await()、awaitNanos(long)、signal()、signalAll()等要领,合营对象锁实例完成守候/关照功用,道理是基于AQS内部类ConditionObject完成Condition接口,线程await后壅塞并进入CLH行列(下面提到),守候其他线程挪用signal要领后被叫醒

(2) CLH行列

CLH行列,CLH是算法提出者Craig, Landin, Hagersten的名字简称

AQS内部维护着一个双向FIFO的CLH行列,AQS依靠它来治理守候中的线程,假如线程猎取同步合作资本失利时,会将线程壅塞,并到场到CLH同步行列;当合作资本余暇时,基于CLH行列壅塞线程并分派资本

CLH的head节点保留当前占用资本的线程,或许是没有线程信息,其他节点保留列队线程信息

CLH中每一个节点的状况(waitStatus)取值以下:

  • CANCELLED(1):示意当前节点已作废调理。当timeout或被中缀(相应中缀的情况下),会触发变动为此状况,进入该状况后的节点将不会再变化
  • SIGNAL(-1):示意后继节点在守候当前节点叫醒。后继节点入队后进入休眠状况之前,会将先驱节点的状况更新为SIGNAL
  • CONDITION(-2):示意节点守候在Condition上,当其他线程挪用了Condition的signal()要领后,CONDITION状况的节点将从守候行列转移到同步行列中,守候猎取同步锁
  • PROPAGATE(-3):同享形式下,先驱节点不仅会叫醒其后继节点,同时也能够会叫醒后继的后继节点
  • 0:新节点入队时的默许状况

(3) 资本同享体式格局

AQS定义两种资本同享体式格局:
Exclusive 独有,只要一个线程能实行,如ReentrantLock
Share 同享,多个线程可同时实行,如Semaphore/CountDownLatch

(4) 壅塞/叫醒线程的体式格局

AQS 基于sun.misc.Unsafe类供应的park要领壅塞线程,unpark要领叫醒线程,被park要领壅塞的线程能相应interrupt()中缀要求退出壅塞

2 基础设想

中心设想思绪:AQS供应一个框架,用于完成依靠于CLH行列的壅塞锁和相干的并发同步器。子类经由历程完成剖断是不是能猎取/开释资本的protect要领,AQS基于这些protect要领完成对线程的列队、叫醒的线程调理战略

AQS还供应一个支撑线程平安原子更新的int范例变量作为同步状况值(state),子类能够依据现实需求,天真定义该变量代表的意义举行更新

经由历程子类从新定义的系列protect要领以下:

  • boolean tryAcquire(int) 独有体式格局尝试猎取资本,胜利则返回true,失利则返回false
  • boolean tryRelease(int) 独有体式格局尝试开释资本,胜利则返回true,失利则返回false
  • int tryAcquireShared(int) 同享体式格局尝试猎取资本。负数示意失利;0示意胜利,但没有盈余可用资本;正数示意胜利,且有盈余资本
  • boolean tryReleaseShared(int) 同享体式格局尝试开释资本,假如开释后许可叫醒后续守候节点返回true,不然返回false

这些要领一直由须要须要调理合作的线程来挪用,子类须以非壅塞的体式格局从新定义这些要领

AQS基于上述tryXXX要领,对外供应以下要领来猎取/开释资本:

  • void acquire(int) 独有体式格局猎取到资本,线程直接返回,不然进入守候行列,直到猎取到资本为止,且悉数历程疏忽中缀的影响
  • boolean release(int) 独有体式格局下线程开释资本,先开释指定量的资本,假如完全开释了(即state=0),它会叫醒守候行列里的其他线程来猎取资本
  • void acquireShared(int) 独有体式格局猎取资本
  • boolean releaseShared(int) 同享体式格局开释资本

以独有形式为例:猎取/开释资本的中心的完成以下:

 Acquire:
     while (!tryAcquire(arg)) {
        假如线程还没有列队,则将其到场行列;
     }

 Release:
     if (tryRelease(arg))
        叫醒CLH中第一个列队线程

到这里,有点绕,下面一张图把上面引见到的设想思绪再从新捋一捋:

特征完成

下面引见基于AQS的对象锁、并发东西的一系列功用特征的完成道理

1 显式猎取

该特征还是以ReentrantLock锁为例,ReentrantLock是可重入对象锁,线程每次要求猎取胜利一次锁,同步状况值state加1,开释锁state减1,state为0代表没有任何线程持有锁

ReentrantLock锁支撑平正/非平正特征,下面的显式猎取特征以平正锁为例

(1) 壅塞守候猎取

基础完成以下:

  • 1、ReentrantLock完成AQS的tryAcquire(int)要领,先推断:假如没有任何线程持有锁,或许当前线程已持有锁,则返回true,不然返回false
  • 2、AQS的acquire(int)要领推断当前节点是不是为head且基于tryAcquire(int)可否取得资本,假如不能取得,则到场CLH行列列队壅塞守候
  • 3、ReentrantLock的lock()要领基于AQS的acquire(int)要领壅塞守候猎取锁

ReentrantLock中的tryAcquire(int)要领完成:

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    // 没有任何线程持有锁
    if (c == 0) {
        // 经由历程CLH行列的head推断没有别的线程在比当前更早acquires
        // 且基于CAS设置state胜利(希冀的state旧值为0)
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            // 设置持有锁的线程为当前线程
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 持有锁的线程为当前线程
    else if (current == getExclusiveOwnerThread()) {
        // 仅仅在当前线程,单线程,不必基于CAS更新
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    // 其他线程已持有锁
    return false;
}

AQS的acquire(int)要领完成

public final void acquire(int arg) {
        // tryAcquire搜检开释能猎取胜利
        // addWaiter 构建CLH的节点对象并入队
        // acquireQueued线程壅塞守候
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        // acquireQueued返回true,代表线程在猎取资本的历程当中被中缀
        // 则挪用该要领将线程中缀标志位设置为true
        selfInterrupt();
}


final boolean acquireQueued(final Node node, int arg) {
    // 标记是不是胜利拿到资本
    boolean failed = true;
    try {
        // 标记守候历程当中是不是被中缀过
        boolean interrupted = false;
        // 轮回直到资本开释
        for (;;) {
            // 拿到先驱节点
            final Node p = node.predecessor();
            
            // 假如先驱是head,即本节点是第二个节点,才有资历去尝试猎取资本
            // 多是head开释完资本叫醒本节点,也能够被interrupt()
            if (p == head && tryAcquire(arg)) {
                // 胜利猎取资本
                setHead(node);
                // help GC
                p.next = null; 
                failed = false;
                return interrupted;
            }
            
            // 须要列队壅塞守候
            // 假如在历程当中线程中缀,不相应中缀
            // 且继续列队猎取资本,设置interrupted变量为true
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

(2) 无壅塞尝试猎取

ReentrantLock中的tryLock()的完成仅仅黑白平正锁完成,完成逻辑基础与tryAcquire一致,差别的是没有经由历程hasQueuedPredecessors()搜检CLH行列的head是不是有其他线程在守候,如许当资本开释时,有线程要求资本能插队优先猎取

ReentrantLock中tryLock()详细完成以下:

public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    // 没有任何线程持有锁
    if (c == 0) {
        // 基于CAS设置state胜利(希冀的state旧值为0)
        // 没有搜检CLH行列中是不是有线程在守候
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 持有锁的线程为当前线程
    else if (current == getExclusiveOwnerThread()) {
        // 仅仅在当前线程,单线程,不必基于CAS更新
        int nextc = c + acquires;
        if (nextc < 0) // overflow,整数溢出
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    // 其他线程已持有锁
    return false;
}

(3) 指定时候内壅塞守候猎取

基础完成以下:

  • 1、ReentrantLock的tryLock(long, TimeUnit)挪用AQS的tryAcquireNanos(int, long)要领
  • 2、AQS的tryAcquireNanos先挪用tryAcquire(int)尝试猎取,猎取不到再挪用doAcquireNanos(int, long)要领
  • 3、AQS的doAcquireNanos推断当前节点是不是为head且基于tryAcquire(int)可否取得资本,假如不能取得且超时时候大于1微秒,则休眠一段时候后再尝试猎取

ReentrantLock中的完成以下:

public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

public final boolean tryAcquireNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    // 假如线程已被interrupt()要领设置中缀 
    if (Thread.interrupted())
        throw new InterruptedException();
    // 先tryAcquire尝试猎取锁 
    return tryAcquire(arg) ||
        doAcquireNanos(arg, nanosTimeout);
}

AQS中的完成以下:

private boolean doAcquireNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    if (nanosTimeout <= 0L)
        return false;
    // 猎取到资本的停止时候   
    final long deadline = System.nanoTime() + nanosTimeout;
    final Node node = addWaiter(Node.EXCLUSIVE);
    // 标记是不是胜利拿到资本
    boolean failed = true;
    try {
        for (;;) {
            // 拿到先驱节点
            final Node p = node.predecessor();
            // 假如先驱是head,即本节点是第二个节点,才有资历去尝试猎取资本
            // 多是head开释完资本叫醒本节点,也能够被interrupt()
            if (p == head && tryAcquire(arg)) {
                // 胜利猎取资本
                setHead(node);
                // help GC
                p.next = null; 
                failed = false;
                return true;
            }
            // 更新盈余超时时候
            nanosTimeout = deadline - System.nanoTime();
            if (nanosTimeout <= 0L)
                return false;
            // 列队是不是须要列队壅塞守候 
            // 且超时时候大于1微秒,则线程休眠到超时时候到了再尝试猎取
            if (shouldParkAfterFailedAcquire(p, node) &&
                nanosTimeout > spinForTimeoutThreshold)
                LockSupport.parkNanos(this, nanosTimeout);

            // 假如线程已被interrupt()要领设置中缀
            // 则不再列队,直接退出   
            if (Thread.interrupted())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

(4) 相应中缀猎取

ReentrantLock相应中缀猎取锁的体式格局是:当线程在park要领休眠中相应thead.interrupt()要领中缀叫醒时,搜检到线程中缀标志位为true,主动抛出非常,中心完成在AQS的doAcquireInterruptibly(int)要领中

基础完成与壅塞守候猎取相似,只是挪用从AQS的acquire(int)要领,改成挪用AQS的doAcquireInterruptibly(int)要领

private void doAcquireInterruptibly(int arg)
    throws InterruptedException {
    final Node node = addWaiter(Node.EXCLUSIVE);
    // 标记是不是胜利拿到资本
    boolean failed = true;
    try {
        for (;;) {
            // 拿到先驱节点
            final Node p = node.predecessor();
            
            // 假如先驱是head,即本节点是第二个节点,才有资历去尝试猎取资本
            // 多是head开释完资本叫醒本节点,也能够被interrupt()
            if (p == head && tryAcquire(arg)) {
                // 胜利猎取资本
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return;
            }
            
            // 须要列队壅塞守候
            if (shouldParkAfterFailedAcquire(p, node) &&
                // 从列队壅塞中叫醒,假如搜检到中缀标志位为true
                parkAndCheckInterrupt())
                // 主动相应中缀
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

2 显式开释

AQS资本同享体式格局分为独有式和同享式,这里先以ReentrantLock为例引见独有式资本的显式开释,同享式背面会引见到

与显式猎取有相似之处,ReentrantLock显式开释基础完成以下:

  • 1、ReentrantLock完成AQS的tryRelease(int)要领,要领将state变量减1,假如state变成0代表没有任何线程持有锁,返回true,不然返回false
  • 2、AQS的release(int)要领基于tryRelease(int)列队是不是有任何线程持有资本,假如没有,则叫醒CLH行列中头节点的线程
  • 3、被叫醒后的线程继续实行acquireQueued(Node,int)或许doAcquireNanos(int, long)或许doAcquireInterruptibly(int)中for(;;)中的逻辑,继续尝试猎取资本

ReentrantLock中tryRelease(int)要领完成以下:

protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    // 只要持有锁的线程才有资历开释锁
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
        
    // 标识是不是没有任何线程持有锁    
    boolean free = false;
    
    // 没有任何线程持有锁
    // 可重入锁每lock一次都须要对应一次unlock
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

AQS中的release(int)要领完成以下:

public final boolean release(int arg) {
    // 尝试开释资本
    if (tryRelease(arg)) {
        Node h = head;
        // 头节点不为空
        // 后继节点入队后进入休眠状况之前,会将先驱节点的状况更新为SIGNAL(-1)
        // 头节点状况为0,代表没有后继的守候节点
        if (h != null && h.waitStatus != 0)
            // 叫醒第二个节点
            // 头节点是占用资本的线程,第二个节点才是首个守候资本的线程
            unparkSuccessor(h);
        return true;
    }
    return false;
}

3 可重入

可重入的完成比较简单,以ReentrantLock为例,重如果在tryAcquire(int)要领中完成,持有锁的线程是不是是当前线程,假如是,更新同步状况值state,并返回true,代表能猎取锁

4 可同享

可同享资本以ReentrantReadWriteLock为例,跟独有锁ReentrantLock的区分重要在于,猎取的时刻,多个线程许可同享读锁,当写锁开释时,多个壅塞守候读锁的线程能同时猎取到

ReentrantReadWriteLock类中将AQS的state同步状况值定义为,高16位为读锁持有数,低16位为写锁持有锁

ReentrantReadWriteLock中tryAcquireShared(int)、tryReleaseShared(int)完成的逻辑较长,重要触及读写互斥、可重入推断、读锁对写锁的妥协,篇幅所限,这里就不睁开了

猎取读锁(ReadLock.lock())重要完成以下

  • 1、ReentrantReadWriteLock完成AQS的tryAcquireShared(int)要领,推断当前线程可否取得读锁
  • 2、AQS的acquireShared(int)先基于tryAcquireShared(int)尝试猎取资本,假如猎取失利,则到场CLH行列列队壅塞守候
  • 3、ReentrantReadWriteLock的ReadLock.lock()要领基于AQS的acquireShared(int)要领壅塞守候猎取锁

AQS中同享形式猎取资本的详细完成以下:

public final void acquireShared(int arg) {
    // tryAcquireShared返回负数代表猎取同享资本失利
    // 则经由历程进入守候行列,直到猎取到资本为止才返回
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}

// 与前面引见到的acquireQueued逻辑基础一致
// 差别的是将tryAcquire改成tryAcquireShared
// 另有资本猎取胜利后将传播给CLH行列上守候该资本的节点
private void doAcquireShared(int arg) {
    final Node node = addWaiter(Node.SHARED);
     // 标记是不是胜利拿到资本
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                int r = tryAcquireShared(arg);
                // 资本猎取胜利
                if (r >= 0) {
                    // 传播给CLH行列上守候该资本的节点                             
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    if (interrupted)
                        selfInterrupt();
                    failed = false;
                    return;
                }
            }
            // 须要列队壅塞守候
            // 假如在历程当中线程中缀,不相应中缀
            // 且继续列队猎取资本,设置interrupted变量为true
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

//  资本传播给CLH行列上守候该资本的节点 
private void setHeadAndPropagate(Node node, int propagate) {
    Node h = head; 
    setHead(node);
    if (propagate > 0 || h == null || h.waitStatus < 0 ||
        (h = head) == null || h.waitStatus < 0) {
        Node s = node.next;
        if (s == null || s.isShared())
            // 开释同享资本
            doReleaseShared();
    }
}

开释读锁(ReadLock.unlock())重要完成以下
ReentrantReadWriteLock中同享资本的开释重要完成以下:

  • 1、ReentrantReadWriteLock完成AQS的tryReleaseShared(int)要领,推断读锁开释后是不是另有线程持有读锁
  • 2、AQS的releaseShared(int)基于tryReleaseShared(int)推断是不是须要CLH行列中的休眠线程,假如须要就实行doReleaseShared()
  • 3、ReentrantReadWriteLock的ReadLock.unlock()要领基于AQS的releaseShared(int)要领开释锁

AQS中同享形式开释资本详细完成以下:

public final boolean releaseShared(int arg) {
    // 许可叫醒CLH中的休眠线程
    if (tryReleaseShared(arg)) {
        // 实行资本开释
        doReleaseShared();
        return true;
    }
    return false;
}
    
private void doReleaseShared() {
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            // 当前节点正在守候资本
            if (ws == Node.SIGNAL) {
                // 当前节点被其他线程叫醒了
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;            
                unparkSuccessor(h);
            }
            // 进入else的前提是,当前节点方才成为头节点
            // 尾节点方才到场CLH行列,还没在休眠前将先驱节点状况改成SIGNAL
            // CAS失利是尾节点已在休眠前将先驱节点状况改成SIGNAL
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;               
        }
        // 每次叫醒后驱节点后,线程进入doAcquireShared要领,然后更新head
        // 假如h变量在本轮轮回中没有被转变,申明head == tail,行列中节点悉数被叫醒
        if (h == head)                 
            break;
    }
}

5 平正、非平正

这个特征完成比较简单,以ReentrantLock锁为例,平正锁直接基于AQS的acquire(int)猎取资本,而非平正锁先尝试插队:基于CAS,希冀state同步变量值为0(没有任何线程持有锁),更新为1,假如悉数CAS更新失利再举行列队

// 平正锁完成
final void lock() {
    acquire(1);
}

// 非平正锁完成
final void lock() {
    // 第1次CAS
    // state值为0代表没有任何线程持有锁,直接插队取得锁
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

// 在nonfairTryAcquire要领中再次CAS尝试猎取锁
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 第2次CAS尝试猎取锁
        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;
}

总结

AQS的state变量值的寄义不肯定代表资本,差别的AQS的继续类能够对state变量值有差别的定义

比方在countDownLatch类中,state变量值代表还需开释的latch计数(能够明白为须要翻开的门闩数),须要每一个门闩都翻开,门才翻开,一切守候线程才会最先实行,每次countDown()就会对state变量减1,假如state变量减为0,则叫醒CLH行列中的休眠线程

进修相似底层源码发起先定几个题目,带着题目进修;浅显进修前发起先明白透辟团体设想,团体道理(能够先浏览相干文档资料),再研讨和源码细节,防止一最先就扎进去源码,轻易无功而返

Up Next:

乡村基建“套路深”,这会不会成为宏观经济的欣喜?

乡村基建“套路深”,这会不会成为宏观经济的欣喜?