1。AQS介绍 相信每个JavaCoder都使用过或者至少听说过AQS,它是抽象队列同步器AbstractQueuedSynchronizer的简称,在juc包下。它提供了一套可用于实现锁同步机制的标准框架,其维护了一个volatile修饰的共享变量state和一个FIFO(先进先出)线程等待队列,多线程争用资源被阻塞的时候就会进入这个队列。state是共享变量,其访问方式有如下三种:getState(),setState(),compareAndSetState(),通过尝试获取共享变量state的结果来对线程的状态作出处理。 基于JDK8分析 我们再简单看下AbstractQueuedSynchronizer类结构:publicabstractclassAbstractQueuedSynchronizerextendsAbstractOwnableSynchronizerimplementsjava。io。Serializable{privatestaticfinallongserialVersionUID7373984972572414691L;Createsanew{codeAbstractQueuedSynchronizer}instancewithinitialsynchronizationstateofzero。protectedAbstractQueuedSynchronizer(){}Waitqueuenodeclass。pToenqueueintoaCLHlock,youatomicallyspliceitinasnewtail。Todequeue,youjustsettheheadfield。preprevheadtailprestaticfinalclassNode{共享节点staticfinalNodeSHAREDnewNode();独占节点staticfinalNodeEXCLUSIVEnull;取消排队staticfinalintCANCELLED1;唤醒后继节点staticfinalintSIGNAL1;这个和Condition相关staticfinalintCONDITION2;waitStatusvaluetoindicatethenextacquireSharedshouldunconditionallypropagatestaticfinalintPROPAGATE3;CLH队列节点Node的等待状态默认值是0取值就是上面的1,1,2,3volatileintwaitStatus;CLH队列节点Node的上一个节点volatileNodeprev;CLH队列节点Node的下一个节点volatileNodenext;CLH队列节点Node维护的ThreadvolatileThreadthread;。。。。。。。。}AQS内部维护的CLH队列中的头节点privatetransientvolatileNodehead;AQS内部维护的CLH队列中的尾节点privatetransientvolatileNodetail;同步状态privatevolatileintstate;。。。。。。省略其他code。。。。。。。。}复制代码 我们用一张图来概括下: 2。AQS原理 AQS是将暂时无法请求共享资源的线程封装成一个CLH队列(虚拟的双向队列)的一个结点来实现锁的分配。根据volatile修饰的state共享变量,线程通过CAS(Compareandswap)去改变状态。如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现,即将暂时获取不到锁的线程加入到队列中等待被唤醒。 3。AQS的实现 AQS(AbstractQueuedSynchronizer)它是整个同步机制的基类,它是基于模板方法模式进行设计的,如果需要实现同步器,一般可以这样:使用者继承AbstractQueuedSynchronizer并重写指定的方法将AQS组合在同步组件的实现中,并调用其模板方法(这些模板方法会调用使用者重写的方法) 同步器在实现的时候只需要实现共享资源state的获取和释放方式即可,具体线程等待队列的维护,AQS已经实现。同步器实现的时候主要关注下面几个方法: 方法 说明 tryAcquire(int) 独占方式。尝试获取资源,成功则返回true,失败则返回false tryRelease(int) 独占方式。尝试释放资源,成功则返回true,失败则返回false tryAcquireShared(int) 共享方式。尝试获取资源。负数表示失败;大于等于0表示成功,其中0表示没有剩余可用资源 tryReleaseShared(int) tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false 一般来说实现同步器要么是独占,要么是共享方式,只需实现tryAcquiretryRelease或者tryAcquireSharedtryReleaseShared中的一种即可。 虽然AOS也支持同步器同时实现独占和共享两种方式,如ReentrantReadWriteLock,但是使用情况较少 其中在acquire(),acquireShared()两种方式下,线程在等待队列中忽略中断,acquirelnterruptibly(),acquireSharedlnterruptibly()支持响应中断,一旦中断将抛出中断异常。3。1独占or共享? 实现了AQS的常见锁有:ReentrantLock、Semaphore、CountDownLatch、CyclicBarrier、ReentrantReadWritelock都是AQS的衍生物。 资源共享方式 典型实现类 独占 只有一个线程能操作共享资源,如ReentrantLock 共享 多个线程可以同时操作共享资源,如Semaphore,CountDownLatch,CyclicBarrier 独占和共享 ReentrantReadWritelock,读锁是共享的,写锁是独占的 好了,关于AQS到这里已经有了基本的认识,接下来我们就从ReentrantLock着手,一步一步分析AQS的源码。4。AQS源码分析4。1认识ReentrantLock 想必ReentrantLock这个类或多或少都使用过,这里我们就先简单看下ReentrantLock类的类结构publicclassReentrantLockimplementsLock,java。io。Serializable{privatestaticfinallongserialVersionUID7373984872572414699L;同步器privatefinalSyncsync;同步器父类:继承了AQSabstractstaticclassSyncextendsAbstractQueuedSynchronizer{privatestaticfinallongserialVersionUID5179523762034025860L;加锁,留给子类实现abstractvoidlock();非公平尝试获取锁finalbooleannonfairTryAcquire(intacquires){finalThreadcurrentThread。currentThread();intcgetState();if(c0){if(compareAndSetState(0,acquires)){setExclusiveOwnerThread(current);returntrue;}}elseif(currentgetExclusiveOwnerThread()){intnextccacquires;Integer。MAXVALUE10if(nextc0)overflowthrownewError(Maximumlockcountexceeded);setState(nextc);returntrue;}returnfalse;}尝试释放锁protectedfinalbooleantryRelease(intreleases){intcgetState()releases;if(Thread。currentThread()!getExclusiveOwnerThread())thrownewIllegalMonitorStateException();booleanfreefalse;if(c0){freetrue;setExclusiveOwnerThread(null);}setState(c);returnfree;}。。。。。。省略其他。。。。。。}非公平同步器,继承了SyncstaticfinalclassNonfairSyncextendsSync{privatestaticfinallongserialVersionUID7316153563782823691L;加锁finalvoidlock(){if(compareAndSetState(0,1))setExclusiveOwnerThread(Thread。currentThread());elseacquire(1);}protectedfinalbooleantryAcquire(intacquires){returnnonfairTryAcquire(acquires);}}公平同步器,继承了SyncstaticfinalclassFairSyncextendsSync{privatestaticfinallongserialVersionUID3000897897090466540L;加锁finalvoidlock(){acquire(1);}尝试获取锁protectedfinalbooleantryAcquire(intacquires){finalThreadcurrentThread。currentThread();intcgetState();if(c0){if(!hasQueuedPredecessors()compareAndSetState(0,acquires)){setExclusiveOwnerThread(current);returntrue;}}elseif(currentgetExclusiveOwnerThread()){intnextccacquires;if(nextc0)thrownewError(Maximumlockcountexceeded);setState(nextc);returntrue;}returnfalse;}}默认是非公平锁publicReentrantLock(){syncnewNonfairSync();}指定公平或者非公平publicReentrantLock(booleanfair){syncfair?newFairSync():newNonfairSync();}ReentrantLock对外提供的加锁方法publicvoidlock(){sync。lock();}支持中断的加锁,一旦线程中断将抛出异常publicvoidlockInterruptibly()throwsInterruptedException{sync。acquireInterruptibly(1);}ReentrantLock对外提供的尝试获取锁,获取到返回true,否则返回falsepublicbooleantryLock(){returnsync。nonfairTryAcquire(1);}可以指定时间,在指定时间内获取锁返回true,否则返回falsepublicbooleantryLock(longtimeout,TimeUnitunit)throwsInterruptedException{returnsync。tryAcquireNanos(1,unit。toNanos(timeout));}ReentrantLock对外提供的释放锁publicvoidunlock(){sync。release(1);}。。。。。。。省略其他。。。。。。}复制代码 从类结构中,可以清晰的看到,它内部是基于AQS来实现的。 我们简单看下类继承关系图: 4。1。1怎么理解公平和非公平? 我们看下公平的方式获取锁的代码: 其实公平和非公平非常好理解,假设张三在银行窗口办理业务,李四和王五在候客区等待,在张三办理完业务的一瞬间,刘华强正好也推门来到银行里办理业务:如果是公平锁,刘华强需要乖乖排队到李四和王五的后面,等他们都办理完了,刘华强再去办理业务如果是非公平锁,刘华强进来后不需要去排队,直接去窗口办理业务,就是这么不礼貌。 如果刘华强顺利的做到了柜台窗口,那么就表示他获得了锁,可以继续办理业务了,如果在刘华强刚要坐下办理时,来了一个更横的白宝山抢先一步做到了柜台椅子上,此时白宝山办起来业务,刘华强则需要去排队了。 知道了公平和非公平,那么我们接下来就以非公平展开讨论,ReentrantLock默认的就是非公平。4。2通过ReentrantLock走进AQS 模拟3个人去银行办理业务publicclassAqsDemo{staticLocklocknewReentrantLock();publicstaticvoidmain(String〔〕args){Threadt1newThread(AqsDemo::bankProcess,张三);t1。start();Threadt2newThread(AqsDemo::bankProcess,李四);Threadt3newThread(AqsDemo::bankProcess,王五);t2。start();t3。start();}模拟银行窗口处理业务privatestaticvoidbankProcess(){lock。lock();try{try{System。out。println(银行柜台开始处理〔Thread。currentThread()。getName()〕的业务);模拟办理业务非常久Thread。sleep(300000L);}catch(InterruptedExceptione){e。printStackTrace();}}finally{lock。unlock();}}}复制代码4。2。1加锁 张三先办理业务,调用lock。lock()方法获得锁,继续往里走:finalvoidlock(){CAS修改同步状态state的值如果成功将state的值从0修改为1,则表示获取锁成功if(compareAndSetState(0,1))setExclusiveOwnerThread(Thread。currentThread());else否则将尝试加入等待队列(这中间也可能会继续抢锁,后面再说)acquire(1);}复制代码 由于张三是第一个来办理业务的,所以它可以成功的将state的值从0修改为1,表示张三获得锁,开始办理业务。 此时李四也过来办理业务,此时发现state1,说明窗口有人在办理,此时李四就尝试去排队了finalvoidlock(){张三正在办理业务,state1if(compareAndSetState(0,1))setExclusiveOwnerThread(Thread。currentThread());else李四将来到这里acquire(1);}复制代码 继续跟进去看下:arg1publicfinalvoidacquire(intarg){if(!tryAcquire(arg)acquireQueued(addWaiter(Node。EXCLUSIVE),arg))selfInterrupt();}复制代码 它内部主要包含了3个方法,我们接下来分别看下这3个方法都做了什么?4。2。1。1tryAcquire(arg)方法剖析finalbooleannonfairTryAcquire(intacquires){finalThreadcurrentThread。currentThread();intcgetState();if(c0){if(compareAndSetState(0,acquires)){setExclusiveOwnerThread(current);returntrue;}}elseif(currentgetExclusiveOwnerThread()){intnextccacquires;if(nextc0)overflowthrownewError(Maximumlockcountexceeded);setState(nextc);returntrue;}returnfalse;}复制代码 ReentrantLock。NonfairSync:非公平锁的具体实现获取同步状态位state(共享资源)的值如果state0,说明共享资源是空闲的,此时通过CAS尝试将其从0修改为1,并标记当前持有锁的线程如果state!0,看下当前线程是否为已经持有锁的线程,如果是,则将state1,表示重入。 整个过程也比较好理解,首先判断state是否为0,比如李四在去候客区之前又过来看下窗口是否是空闲的,如果是空闲的,他就不用去候客区了,直接办理业务就好了(此时就表示李四获得了锁) 如果没有获得锁,没有获得锁具体的体现就是无法将state从0修改为1,所以李四在这里将返回false。4。2。1。2addWaiter(Node。EXCLUSIVE)方法剖析mode:Node。EXCLUSIVEprivateNodeaddWaiter(Nodemode){1。创建一个Node节点,里面封装了当前线程NodenodenewNode(Thread。currentThread(),mode);判断尾节点会否为空,首次进来肯定是空的所以李四过来的时候这里是空,将先调用enq方法第一个排队的节点不会来到这里,它要先调用enq方法去初始化头节点等王五过来的时候我们在分析Nodepredtail;if(pred!null){node。prevpred;if(compareAndSetTail(pred,node)){pred。nextnode;returnnode;}}enq(node);returnnode;}复制代码 继续看下enq(node)方法:node是封装了当前线程的节点privateNodeenq(finalNodenode){死循环for(;;){Nodettail;判断尾节点是否为空,如果为空,必须初始化所以第一次进来(李四过来)的时候讲初始化,初始化的逻辑是设置一个头节点,这个节点没有任何数据,通常称为虚拟节点傀儡节点if(tnull){Mustinitializeif(compareAndSetHead(newNode()))tailhead;}else{node。prevt;将当前节点设置到尾节点if(compareAndSetTail(t,node)){将当前节点和虚拟节点建立联系t。nextnode;returnt;}}}}复制代码 这个方法也比较好理解,第一次循环过来将设置虚拟头节点,然后继续循环,这次tnull就不成立了,于是将来到else的逻辑,在这里将建立当前节点和虚拟节点的联系 用一张图来看下: 到这里,排队节点就建立好双向连接的关系,接下来我们看最后一个方法4。2。1。3acquireQueued(addWaiter(Node。EXCLUSIVE),arg))方法剖析node是封装了李四线程的节点arg1finalbooleanacquireQueued(finalNodenode,intarg){入队失败的标识booleanfailedtrue;try{中断的标识booleaninterruptedfalse;死循环for(;;){获取当前节点的前驱节点finalNodepnode。predecessor();如果前一个节点是head,那么当前节点再真正去候客区前再次尝试获取锁tryAcquire(arg)方法就是我们前面分析的第一个方法,就是尝试获取锁根据我们前面画的那张图可以知道,李四的前驱节点就是head,所以它会再次尝试获取锁,如果张三的业务办理时间特别短,李四获取锁成功则将李四节点置为新的虚拟节点如果张三的业务办理时间特别长,李四获取锁失败,则将调用下面的if方法if(pheadtryAcquire(arg)){setHead(node);p。nextnull;helpGCfailedfalse;returninterrupted;}shouldParkAfterFailedAcquire()方法单独分析if(shouldParkAfterFailedAcquire(p,node)parkAndCheckInterrupt())interruptedtrue;}}finally{if(failed)cancelAcquire(node);}}复制代码 我们先看第一个if的逻辑:finalNodepnode。predecessor();if(pheadtryAcquire(arg)){setHead(node);p。nextnull;helpGCfailedfalse;returninterrupted;}复制代码 它的逻辑就是:获取当前节点的前驱节点,如果前驱节点是head,那说明当前节点是排在head之后的第一个节点,那么此时当前就去再次尝试获取锁,如果获取锁成功,则将当前节点置为傀儡节点。 如果当前节点的前驱节点不是head节点或者尝试获取锁失败,则将第二个if的逻辑:if(shouldParkAfterFailedAcquire(p,node)parkAndCheckInterrupt()){interruptedtrue;}复制代码 我们先看第一个方法:shouldParkAfterFailedAcquire(p,node)pred:是当前节点的前驱节点node:当前节点privatestaticbooleanshouldParkAfterFailedAcquire(Nodepred,Nodenode){前驱节点的状态waitStatus的值默认是0,有1,1,2,3几种取值intwspred。waitStatus;如果前驱节点的状态是1,则直接返回true,表示pred节点具有唤醒下一个节点的责任if(wsNode。SIGNAL)returntrue;如果ws0,表示前驱节点已经取消了,还是以银行为例,可能某个在候客区排队的人突然临时有事,就不办理了,此时他就从候客区出去了if(ws0){如果(当前节点的)前驱节点是取消状态,那么我就继续看前驱节点的前驱节点是不是也是取消的直到找到有效节点举个例子:ABCDEF假如当前节点node是E,前驱节点是D,如果D的状态是1(CANCELLED),那么就看C的状态,如果C也是1,那么就看B,如果B的状态不是1,则结构就变成了ABEFdo{node。prevpredpred。prev;}while(pred。waitStatus0);pred。nextnode;}else{状态是小于等于0并且不是1使用CAS将前一个节点的状态修改为1compareAndSetWaitStatus(pred,ws,Node。SIGNAL);}returnfalse;}复制代码 总的来说,这个方法就是将当前节点的前驱节点的waitStatus状态修改为Node。SIGNAL,而这个状态表示前驱结点具有唤醒下一个节点的能力。 由于现在CLH队列中只有两个节点:虚拟头节点和李四节点,那么这个方法执行完就变成了这样: 接下来我们再看第二个方法:parkAndCheckInterrupt()privatefinalbooleanparkAndCheckInterrupt(){1。挂起当前线程LockSupport。park(this);2。返回当前线程是否中断了注意:只有当前线程被unpark()或者当前线程被中断了,才会走到这里,否则将一直阻塞在1处returnThread。interrupted();}复制代码 这个方法比较简单,首先通过LockSupport的park()方法挂起当前线程,此时到这里,李四线程就是真正的在候客区排队等待了。之后只有被唤醒了才有资格去柜台办理业务。 我们在回过来后,整体看下这个方法:finalbooleanacquireQueued(finalNodenode,intarg){booleanfailedtrue;try{booleaninterruptedfalse;for(;;){finalNodepnode。predecessor();if(pheadtryAcquire(arg)){setHead(node);p。nextnull;helpGCfailedfalse;returninterrupted;}if(shouldParkAfterFailedAcquire(p,node)parkAndCheckInterrupt())interruptedtrue;}}finally{if(failed)cancelAcquire(node);}}复制代码为什么要搭配死循环?acquireQueued()方法的出口在哪里? 我们知道,同步队列是一个先进先出的队列,headABCDtail,那么A肯定是第一个要被唤醒的线程(唤醒后面我们再看)。我们还是以李四为例,李四的前驱结点是head,但是张三的业务办理时间特别长,所以李四调用tryAcquire(arg)方法依然没有获得锁,那么李四最后将会调用LockSupport。park()方法将自己挂起,阻塞到这里,无法向下执行,所以也不会退出for循环,更不会退出acquireQueued()方法。假设李四被LockSupport。unpark()唤醒了,唤醒之后,将继续执行for循环,再次判断if(pheadtryAcquire(arg))是否成立,其实主要就是判断tryAcquire(arg)方法是否可以获得锁,如果获得锁,则返回true,否则将返回false。如果李四获得锁((pheadtryAcquire(arg))true),那么李四就成为新的head节点(傀儡节点),李四将退出for循环,也就意味着退出acquireQueued()方法,这就表明李四获得了锁,意味着加锁逻辑lock。lock()可以继续往下走了。如果李四没有获得锁((pheadtryAcquire(arg))false),什么情况下李四被唤醒之后没有获得锁呢?这就是前面我们说过的非公平锁,李四被唤醒后去抢锁,可能在抢的一瞬间,有田七过来率先获得了锁,所以李四就没有获得锁,这样李四就需要继续挂起,等待被唤醒。(这就是需要放到死循环中的原因) 我们继续看王五抢锁(张三办理业务时间特别长,一直没有释放锁,李四已经在候客区排队等待了) 整个过程和李四基本上是一样的,我们只看下不同的地方:addWaiter(Nodemode)modeNode。EXCLUSIVEprivateNodeaddWaiter(Nodemode){将当前线程(王五)封装到Node节点中NodenodenewNode(Thread。currentThread(),mode);如果pred不是null,说明队列已经初始化过了,所以就不需要进入enq()方法去初始化队列这里只需要使用CAS将当前节点设置到尾部即可,然后返回当前节点Nodepredtail;if(pred!null){node。prevpred;if(compareAndSetTail(pred,node)){pred。nextnode;returnnode;}}enq(node);returnnode;}复制代码 继续往下看:acquireQueued(finalNodenode,intarg)finalbooleanacquireQueued(finalNodenode,intarg){booleanfailedtrue;try{booleaninterruptedfalse;for(;;){现在王五的前驱结点是李四,李四不是head结点,所以不会进来这里finalNodepnode。predecessor();if(pheadtryAcquire(arg)){setHead(node);p。nextnull;helpGCfailedfalse;returninterrupted;}shouldParkAfterFailedAcquire(p,node):修改前驱结点李四的waitStatus为1,表示具有唤醒后继节点的能力parkAndCheckInterrupt():当前线程挂起if(shouldParkAfterFailedAcquire(p,node)parkAndCheckInterrupt())interruptedtrue;}}finally{if(failed)当前线程因为某种原因失败了,则取消排队,基本上不会进来这里cancelAcquire(node);}}复制代码 到这里,入队的逻辑就基本上都完成了,接下来我们看下唤醒。4。2。2解锁(唤醒) 张三业务办理结束,需要释放锁,然后去唤醒等待队列中的线程。privatestaticvoidbankProcess(){lock。lock();try{try{System。out。println(银行柜台开始处理〔Thread。currentThread()。getName()〕的业务);Thread。sleep(300000L);}catch(InterruptedExceptione){e。printStackTrace();}}finally{业务办理完毕,释放锁lock。unlock();}}复制代码 我们继续看下内部逻辑,主要做两件事:当前线程释放锁:将同步状态位state从1置为0唤醒等待队列中的线程去抢锁:用LockSupport。unpark()唤醒LockSupport。park()阻塞的线程publicfinalbooleanrelease(intarg){1。tryRelease(arg)释放锁,释放成功,去唤醒等待中的线程if(tryRelease(arg)){Nodehhead;if(h!nullh。waitStatus!0)2。唤醒等待中的线程unparkSuccessor(h);returntrue;}returnfalse;}复制代码 首先看下释放锁的逻辑:就是将同步状态位state从1修改为0。releases1protectedfinalbooleantryRelease(intreleases){获取同步状态位的值,然后减一intcgetState()releases;if(Thread。currentThread()!getExclusiveOwnerThread())thrownewIllegalMonitorStateException();booleanfreefalse;如果为0,说明成功释放锁if(c0){freetrue;将独占线程置为nullsetExclusiveOwnerThread(null);}将同步状态位的值从1修改为0setState(c);returnfree;}复制代码 然后再看下唤醒等待队列中的线程的逻辑node是等待队列中的头结点同步队列是一个FIFO的队列,所以肯定要先从头部唤醒unparkSuccessor方法名直译过来就是唤醒后继结点,head是傀儡节点,它的next才是有效节点privatevoidunparkSuccessor(Nodenode){经过前面的分析,我们知道node(head)的waitStatus是1,所以这里if条件成立然后通过CAS将其修改为0,不明白这里为什么要置为0?intwsnode。waitStatus;if(ws0){compareAndSetWaitStatus(node,ws,0);}获得head节点的下一个节点,这个节点就是李四Nodesnode。next;如果s是null或者waitStatus0,说明下一个节点可能不存在了或者取消排队了此时就从队列的尾部一直往前找,直到找到最接近head的一个有效节点if(snulls。waitStatus0){snull;for(Nodettail;t!nullt!node;tt。prev)if(t。waitStatus0)st;}if(s!null){找到之后,就唤醒等待队列中的线程LockSupport。unpark(s。thread);}}复制代码 一旦调用了LockSupport。unpark(s。thread)方法,那么同步队列中调用LockSupport。park()方法阻塞的线程将会被唤醒,从阻塞之处开始往下执行。 我们看下阻塞的代码:finalbooleanacquireQueued(finalNodenode,intarg){booleanfailedtrue;try{booleaninterruptedfalse;for(;;){finalNodepnode。predecessor();if(pheadtryAcquire(arg)){setHead(node);p。nextnull;helpGCfailedfalse;returninterrupted;}parkAndCheckInterrupt()调用LockSupport。park()方法阻塞线程if(shouldParkAfterFailedAcquire(p,node)parkAndCheckInterrupt()){interruptedtrue;}}}finally{if(failed)cancelAcquire(node);}}复制代码 一旦执行LockSupport。park()方法,现成将阻塞到这里,不会往下执行,一直卡在if条件判断里,一旦有人释放锁,然后调用LockSupport。unpark(s。thread)方法唤醒线程,则这里就可以继续往下执行,由于这里是一个for(;;)的死循环,所以它继续循环执行,然后首先执行第一个if判断,看它是否是接挨着head节点(head节点是虚拟节点,不保存线程)的节点(先进先出的体现),然后尝试获取锁(将同步状态位state从0修改为1)一旦获取锁,则存储当前线程的Node节点将成为新的虚拟头节点,然后退出acquireQueued(finalNodenode,intarg)方法,一直向上退出,最终来到我们自己编写的加锁代码lock。lock()处,然后就可以继续往下执行业务逻辑了。 我们把其中的代码用图标注下: 到这里,整个AQS的核心流程就结束了,其实只要一步一步仔细的梳理代码,发现也并不难理解O()O哈哈5。总结 我们简单梳理下AQS的加锁,排队,唤醒的流程:通过CAS将同步状态位state从0修改为1,如果修改成功,表示获得锁;如果修改失败,则表示锁被其他线程占有,此时需要排队排队的逻辑就是将当前线程封装到Node节点中,然后尾插法插入到CLH同步队列的尾部,然后调用LockSupport。park()阻塞当前线程获得锁的线程释放锁后,将会调用LockSupport。unpark()方法从头部开始唤醒阻塞的线程(先进先出的体现) 好了,关于AQS的介绍就到这里吧,后面有问题再补充。