AQS原理分析
什么是AQS
java.util.concurrent包中的大多数同步器实现都是围绕着共同的基础行为,比如等待队列、条件队列、独占获取、共享获取等,而这些行为的抽象就是基于 AbstractQueuedSynchronizer(简称AQS)实现的,AQS是一个抽象同步框架,可以用来实现一个依赖状态的同步器。
JUC中提供的大多数的同步器如Lock, Latch,都是基于AQS框架来实现的
- 一般是通过定义一个非公开的内部类辅助类继承AQS,用于实现同步器的同步属性。
- 将同步器所有调用都映射到内部辅助类对应的方法。
比如CountDownLatch内部实现的同步辅助类:
AQS的一些实现类:
AQS框架实现思想
既然AbstractQueuedSynchronizer 带有queue,那一定和队列密不可分了,其实AQS和synchornized一样,都是借助java管程模型实现的同步器。
AQS定义两种队列 同步等待队列: 主要用于维护获取锁失败时入队的线程
条件等待队列: 调用await()的时候会释放锁,然后线程会加入到条件队列,调用 signal()唤醒的时候会把条件队列中的线程节点移动到同步队列中,等待再次获得锁。
公平/非公平则表示锁被释放后,新晋新线程可以不用排队就可以竞争并获取锁时,对应该图则是,当前线程释放锁后,会唤醒队列中后继节点的线程,同时新晋线程也可以竞争锁,如果新晋线程竞争锁成功,那么新晋线程即为头节点,失败则入队成为尾节点阻塞等待。
AQS框架中主要属性方法
AQS队列由内部类Node以链表的方式组成,先看下这个Node的主要属性。 首先一定会有上一个节点及下一个节点的指针,即prev,next;thread代表当前Node代表的线程;等待状态waitStatus:
- 值为0,初始化状态,表示当前节点在sync队列中,等待着获取锁。
- CANCELLED,值为1,表示当前的线程被取消;
- SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;
- CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列 中;
- PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;
AQS主要属性:head,tail 表示AQS队列的头尾节点,如果获取不到锁入队,则添加至尾节点后。 state 表示锁定状态,通过cas竞争此共享变量获取/释放锁,State可以获取,设置以获取当前状态及更改状态;
AQS具备的特性:阻塞等待队列 共享/独占 公平/非公平 可重入 允许中断
AQS定义两种资源共享方式 Exclusive-独占,只有一个线程能执行,如ReentrantLock Share-共享,多个线程可以同时执行,如Semaphore/CountDownLatch
AQS定义两种队列 同步等待队列: 主要用于维护获取锁失败时入队的线程
条件等待队列: 调用await()的时候会释放锁,然后线程会加入到条件队列,调用 signal()唤醒的时候会把条件队列中的线程节点移动到同步队列中,等待再次获得锁
AQS 定义了5个队列中节点状态:
不同的自定义同步器竞争共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源state的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了。自定义同步器实现时主要实现以下几种方法:
isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。 tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但 没有剩余可用资源;正数表示成功,且有剩余资源。
tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待 结点返回true,否则返回false。
AQS主要代码详解
构建同步等待队列方法:
/**
* Creates and enqueues node for current thread and given mode.
*
* @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
* @return the new node
*/
private Node addWaiter(Node mode) {
// 获取锁失败,创建新节点 传入当前线程
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
// 获取尾节点,CAS成功置为尾节点(防止并发)
if (compareAndSetTail(pred, node)) {
// 则把新节点添加链表最后
pred.next = node;
return node;
}
}
// 如果头节点还未初始化或者compareAndSetTail失败走enq逻辑
enq(node);
return node;
}
/**
* Inserts node into queue, initializing if necessary. See picture above.
* @param node the node to insert
* @return node's predecessor
*/
private Node enq(final Node node) {
// 自旋逻辑,乐观锁,自旋直至成功
for (;;) {
Node t = tail;
// 头节点未初始化则设置头节点且头尾相同
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
// 这里对于上面compareAndSetTail失败逻辑,只是变为自旋直到成功
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}