1。什么是AQS2。它能做什么3。为什么AQS是JUCcontent4最重要的基石。AQS内部架构5.来自我们ReentrantLockAQS6的解读。总结1.什么是AQS?AQS——全称AbstractQueuedSynchronizer,抽象队列同步器。我们可以看一下源码中的解释:意思是用来构建一个重量级的锁或者其他同步器组件的基础框架,是整个JUC的基石。它通过内置的FIFO队列完成资源获取线程的排队工作,并用一个int类变量来表示持有锁的状态。简而言之1)这是一个基石框架(模板方法设计模式)2)就是把获取资源的线程排队,让它有序的获取资源。2.我们能做什么?假设我们在使用ReentrantLock的时候,一定会有线程阻塞。如果有线程阻塞,我们必须排队,我们需要一个队列。抢占资源失败的线程继续等待,但是等待的线程仍然保留获取锁的可能性,获取锁的过程继续进行。这是AQS。我们实现了锁和队列的构造,让线程有序的获取资源。3、为什么AQS是JUC内容最重要的基石我们先来看看具体实现AQS的类:ReentrantLock、CountDownLatch、ReentrantReadWriteLock、Semaphore等。此时我们需要理清一个关系:锁:面向所有锁用户,定义了程序员和锁交互的应用层api,隐藏了实现细节,开发者可以调用。Synchronizer:面向锁的实现者,如并发大师DougLee,提出了一套规范,简化了锁的实现,屏蔽了同步状态的管理,阻塞的线程排队和通知,唤醒机制等。4、AQS的内部架构我们点进源码看看:下面我们来搜索变量/***Thesynchronizationstate.*/privatevolatileintstate;AQS使用上面的volatileint类型变量来表示占用锁的同步状态,然后搜索下面这个变量:staticfinalclassNode{//还有一些变量没有释放,重点解释这些volatileNodeprev;volatileNodenext;volatileThreadthread;//....}AQS就是利用这个链表的FIFO对抗来完成资源获取的排队工作,将每个要抢占的线程封装成一个Node节点,实现锁的分配,并通过CAS完成状态值的修改。也就是说,我们通过队列(需要排队的线程的管理)+状态变量(公共资源类的管理)来实现AQS的基本结构。我们再来看看Node的内部:volatileintwaitStatus;Node通过waitState的成员变量阻塞或唤醒队列中的线程。waitStatus的状态如图所示,不过也可以看到源码注释的结尾。下面用一张图来展示一下,AQS的基本结构5.从我们的ReentrantLock解读AQS下面我们以从ReentrantLock来解读AQS的源码作为切入点。ReentrantLocklock=newReentrantLock();lock.lock();try{TimeUnit.MINUTES.sleep(60);}catch(InterruptedExceptione){e.printStackTrace();}最后{lock.unlock();}我们点击lock,可以看到lock的底层是调用一个sync变量的lock方法publicvoidlock(){sync.lock();}andsync:abstractstaticclassSyncextendsAbstractQueuedSynchronizer{//...}可以看出,Lock接口的实现类基本上是通过聚合一个队列同步器的子类来完成线程访问控制的。我们继续点击锁里面,会发现有公平锁和非公平锁:然后我们继续点击里面查看抢占锁的acquire方法,会发现会有区别图片。需要判断队列前面是否有排队的线程。公平锁抢占资源时,需要判断队列前是否有排队线程。所以其实我们只需要了解非公平锁的源码,其实我们也了解公平锁的源码。下面梳理一下获取锁的整体流程:先从NonfairSync的lock方法说起:/***执行锁定。尝试立即插入,备份到正常*失败时获取。*/finalvoidlock(){//当前线程尝试将锁资源的状态从0变为1//如果成功//将资源占用的线程设置为本线程if(compareAndSetState(0,1))setExclusiveOwnerThread(Thread.currentThread());else//如果失败//取acquire方法acquire(1);}protectedfinalbooleantryAcquire(intacquires){returnnonfairTryAcquire(acquires);}}继续查看acquire(1)方法publicfinalvoidacquire(intarg){if(!tryAcquire(arg)&&acquireQueued(addWaiter(Node.EXCLUSIVE),arg))selfInterrupt();}tryAcquire:protectedfinalbooleantryAcquire(intacquires){//点击该方法进入后面的方法returnnonfairTryAcquire(acquires);}finalbooleannonfairTryAcquire(intacquires){//先获取当前线程finalThreadcurrent=Thread.currentThread();intc=getState();//如果锁当前状态为0(未占用状态)if(c==0){//尝试使用cas占用if(compareAndSetState(0,acquires)){//设置占用资源的线程作为基础ThreadsetExclusiveOwnerThread(current);返回真;}}//如果当前锁状态不为0,但是占用锁的线程是本线程elseif(current==getExclusiveOwnerThread()){//相当于一个可重入锁那么状态标志+1intnextc=c+获取;if(nextc<0)//溢出thrownewError("Maximumlockcountexceeded");设置状态(下一步);返回真;}返回假;}addWaiter:私有NodeaddWaiter(Nodemode){//设置当前线程为一个NodeNodenode=newNode(Thread.currentThread(),mode);//尝试enq的快速路径;backuptofullenqonfailure//如果结束节点不为空Nodepred=tail;if(pred!=null){//设置本线程的前一个节点为尾节点node.prev=pred;//使用cas交换tail节点if(compareAndSetTail(pred,node)){pred.next=node;返回节点;}}//如果尾节点为空,则初始化enq(node);返回节点;}privateNodeenq(finalNodenode){//无限循环for(;;){Nodet=tail;//如果tail节点为空,创建一个puppet节点if(t==null){//必须初始化if(compareAndSetHead(newNode()))tail=head;}else{//如果尾节点不为空,交换尾节点返回,跳出循环node.prev=t;if(compareAndSetTail(t,node)){t.next=node;返回吨;}}}}acquireQueued:finalbooleanacquireQueued(finalNodenode,intarg){booleanfailed=true;尝试{布尔中断=false;对于(;;){最终节点p=node.predecessor();//如果node的前一个节点为头节点(队列前没有其他节点),则抢锁成功if(p==head&&tryAcquire(arg)){//设置当前节点为头节点头节点setHead(node);//原头节点的下一个节点为空,帮助GCp.next=null;//帮助GCfailed=false;返回中断;}//如果该节点的前一个节点不是cast节点//改变当前节点的运行状态if(shouldParkAfterFailedAcquire(p,node)&&//暂停线程parkAndCheckInterrupt())interrupted=true;}}最后{如果(失败)取消l获取(节点);}}selfInterrupt:返回本线程的中断状态staticvoidselfInterrupt(){Thread.currentThread().interrupt();}再看unlock方法:publicfinalbooleanrelease(intarg){//尝试释放锁,如果锁的状态为0,则成功释放if(tryRelease(arg)){Nodeh=头;if(h!=null&&h.waitStatus!=0)//唤醒头节点unparkSuccessor(h);返回真;}返回假;}6.总结今天我们了解AQS的概念和源码阅读。AQS作为整个JUC的基石框架,非常重要
