AQS
- AQS简介
- AQS实现原理
- 场景01-线程抢夺锁失败时,AQS队列的变化
- 场景02-线程被唤醒时,AQS队列的变化
AQS简介
AQS(全称AbstractQueuedSynchronizer)即队列同步器。它是构建锁或者其他同步组件的基础框 架(如ReentrantLock、ReentrantReadWriteLock、Semaphore等)。AQS是JUC并发包中的核心基础组件,其本身是一个抽象类。理论上还是利用管程实现的,在AQS中,有一个volatile修饰的state,获取 锁的时候,会读写state的值,解锁的时候,也会读写state的值。所以AQS就拥有了volatile的happens- before规则。加锁与解锁的效果上与synchronized是相同的。
由类图可以看到,AQS是一个FIFO的双向队列,其内部通过节点head和tail记录队首和队尾元素,队列 元素的类型为Node。
在AQS中维持了一个单一的状态信息state,对于ReentrantLock的实现来说,state 可以用来表示当前线 程获取锁的可重入次数;AQS继承自AbstractOwnableSynchronizer,其中的exclusiveOwnerThread 变量表示当前共享资源的持有线程。
AQS实现原理
- AQS是一个同步队列,内部使用一个FIFO的双向链表,管理线程同步时的所有被阻塞线程。双向链表这种数据结构,它的每个数据节点中都有两个指针,分别指向直接后继节点和直接前驱节点。所以,从双向链表中的任意一个节点开始,都可以很方便地访问它的前驱节点和后继节点。
- 我们看下面的AQS的数据结构,AQS有两个节点head,tail分别是头节点和尾节点指针,默认为null。 AQS中的内部静态类Node为链表节点,AQS会在线程获取锁失败后,线程会被阻塞并被封装成Node加 入到AQS队列中;当获取锁的线程释放锁后,会从AQS队列中的唤醒一个线程(节点)。
场景01-线程抢夺锁失败时,AQS队列的变化
-
AQS的head,tail分别代表同步队列头节点和尾节点指针,默认为null。
-
当第一个线程抢夺锁失败,同步队列会先初始化,随后线程会被封装成Node节点追加到AQS队列 中。假设当前独占锁的的线程为ThreadA,抢占锁失败的线程为ThreadB。
场景02-线程被唤醒时,AQS队列的变化
ReentrantLock唤醒阻塞线程时,会按照FIFO的原则从AQS中head头部开始唤醒首个节点中线程。 head节点表示当前获取锁成功的线程ThreadA节点。
当ThreadA释放锁时,它会唤醒后继节点线程ThreadB,ThreadB开始尝试获得锁,如果ThreadB获得 锁成功,会将自己设置为AQS的头节点。ThreadB获取锁成功后,AQS变化如下:
- head指针指向ThreadB节点。
- 将原来头节点的next指向Null,从AQS中删除。
- 将ThreadB节点的prev指向Null,设置节点的thread=null。