AQS作为JUC并发组件实现的核心。全称是AbstructQueuedSynchronizer,也就是同步队列器。
其内部实现主要的是一个state状态标识和基于FIFO一个同步队列。
这个状态标识表明当前的资源是否是可以争取的,当state=0是,表示资源没有加锁;当state>0时,表示资源加锁。
关于这个队列,有几个属性:
1)队列是基于双向链表的,因此具有定制的节点结构信息。node
2)tail尾指针,指向队列的最后一个节点
3)头指针,指向队列的第一个节点(也即是 头节点)。头结点中包含有当前资源的同步状态信息。
4)waitStatus 节点等待标识。
=1时 SIGNAL 表明当前节点的后继节点所包含的线程需要运行。
=-1 CANCEL 表明当前节点线程取消
=-2 CONDITION 表明当前节点在等待condition 处于Condition等待队列
=-3 PROPAGATE 应用在共享锁中
= 0 当前节点在同步队列中,等待获取锁
AQS是通过内部的同步队列来完成线程获取资源的排队工作
AQS可以实现独占锁和共享锁。
ReentrantLock实现的是独占锁;ReentrantReadWriteLock实现的是独占锁和共享锁;CountDownLatch实现的是共享锁。
因此,关于锁的获取和释放有以下几种方式:
1)独占式获取锁
首先,线程尝试获得锁tryAcquire,获取成功则函数返回,获得失败,addWaiter构造一个独占模式同步节点,如果前驱节点不为空则将该节点采用CAS方式放置到同步队列尾部,如果放置失败则说明当前节点位置存在并发竞争,进入enq方法进行添加,其实,enq方法也就是通过CAS自旋锁的方式不断地尝试来向队列中添加构造的节点;
如果节点最后成功地放置到队列里。执行acquireQueued方法,当前节点中的线程通过自旋方式尝试获取同步状态。
如果当前节点的前驱节点是头结点,并且重新尝试获取锁成功,就将当前节点设置为头结点!否则判断当前线程是否可以被安全阻塞挂起(根据waitStatus来判断,判断函数shouldParkAfterFailedAcquire),是的话就调用parkAndCheckInterrupt方法里的park方法进行挂起。然后再次进入for循环自旋。
如何判断????
当前节点的前驱节点是waitStatus是SIGNAL则可以挂起,之后由前驱节点进行唤醒;
若前驱节点是CANCEL 则向前遍历直到找到一个是SIGNAL的节点,并将当前节点放置在此节点的后面,不能挂起;
否则尝试将前驱节点的状态设置为SIGNAL,不能挂起
2)独占式释放锁
调用release方法来释放锁。这个方法一般由子类自己重写。
首先,尝试释放锁tryRelease,如果成功那么就唤醒后继节点 unparkSuccessor里的LockSupport.unpark方法
3)共享式获得锁
调用acquireShared方法,首先尝试获得锁,获得成功就返回;获得失败进入等待队列。直到获取到资源为止才返回。
若当前节点的前驱节点是头结点,则尝试进行加锁;如果加锁成功就调用setHeadAndPropagate,将当前节点设置为头结点,唤醒后继节点,并将之前的头结点从队列中踢出去,等待GC回收;
shared模式下是允许多个线程持有一把锁的,其中tryAcquire的返回值标志了是否允许其他线程继续进入。如果允许的话,需要唤醒队列中等待的线程。其中setHeadAndPropagate方法中的doReleaseShared方法的逻辑很简单,就是唤醒后继线程。
如果获得锁失败,判断当前线程是否可以被安全阻塞挂起,是的话就调用parkAndCheckInterrupt方法里的park方法进行挂起。
tryAcquireShared返回值为正,说明获得锁成功,负数则失败。
4)共享式释放锁
调用releaseShared方法;尝试释放锁,释放成功再次调用doReleaseShared,此方法内部还是调用LockSupport.unpark()方法唤醒后继线程。
5)独占超时获得锁
相比于独占获得锁方式,最重要的区别就是:独占获得锁在自旋获得锁失败之后会挂起,让出CPU时间片,之后再次进入for循环自旋尝试获得锁;而独占超时获得锁在获得锁失败之后,判断当前时间是否超时?若超时,则直接返回不再进行for循环;若没有超时,则将当前线程阻塞指定的超时时间之后再次进行for循环进行CAS自旋获取锁!
调用doAcquireNanos方法。该方法在自旋过程中,当节点的前驱节点为头节点时尝试获取同步状态,如果获取成功则从该方法返回,这个过程和独占式同步获取的过程类似,
但是在同步状态获取失败的处理上有所不同。如果当前线程获取同步状态失败,则首先重新计算超时间隔nanosTimeout,则判断是否超时(nanosTimeout小于等于0表示已经超时),如果没有超时,则使当前线程等待nanosTimeout纳秒(当已到设置的超时时间,该线程会从LockSupport.parkNanos(Object blocker,long nanos)方法返回)。
如果nanosTimeout小于等于spinForTimeoutThreshold(1000纳秒)时,将不会使该线程进行超时等待,而是进入快速的自旋过程。原因在于,非常短的超时等待无法做到十分精确,如果这时再进行超时等待,相反会让nanosTimeout的超时从整体上表现得反而不精确。因此,在超时非常短的场景下,同步器会进入无条件的快速自旋。