一.AbstraactQueuedSynchronizer(AQS)
1.队列同步器,提供了一种实现阻塞锁和一系列依赖FIFO等待队列的同步器的框架
2.基于模板方法模式
3.使用方式:继承该类作为一个内部辅助类实现同步原语 并重写指定方法
4.实现思路:AQS内部维护一个CLH队列来管理锁 双向链表的队列。线程会首先
尝试获取锁,若失败,则将当前线程以及等到状态等信息包装成一
个Node节点,添加到同步队列中 接着不断尝试获取当前锁(前
提条件:当前节点是head节点的直接后继才会尝试),若失败会
阻塞自己,直至被唤醒,而当持有锁的线程被释放时,会唤醒队列
中的后继线程
5.实现的三件事:同步状态的管理,线程的阻塞与唤醒 同步队列的维护
二.Node节点类
/**
* 总的来说;保存节点状态,前驱节点 后继节点 线程
*/
static final class Node {
/**
* 用于标记一个节点在共享模式下等待
*/
static final Node SHARED = new Node();
/**
* 用于标记一个节点在独占模式下等待
*/
static final Node EXCLUSIVE = null;
/**
* 等待状态:取消 当前线程因为取消或者中断被取消 终结状态
*/
static final int CANCELLED = 1;
/**
* 等待状态:通知 当前线程的后继线程被阻塞或者即将被阻塞,当前线程释放锁或者取消后需要唤醒后继线程 一般是后继线程来设置先去线程
*/
static final int SIGNAL = -1;
/**
* 等待状态:条件等待 当前线程在Condition队列当中
*/
static final int CONDITION = -2;
/**
* 等待状态:传播 用于将唤醒后继线程传递下去 是为了完善和增强共享锁的唤醒机制 在一个节点成为头结点之前 是不会变成此状态
*/
static final int PROPAGATE = -3;
/**
* 等待状态 0:表示无状态
*/
volatile int waitStatus;
/**
* 前驱节点
*/
volatile Node prev;
/**
* 后继节点
*/
volatile Node next;
/**
* 节点对应的线程
*/
volatile Thread thread;
/**
* 等待队列中的后继节点
*/
Node nextWaiter;
/**
* 判断当前节点是否在共享模式下等待
*/
final boolean isShared() {
return nextWaiter == SHARED;
}
/**
* 获取前驱节点 若为空抛出空指针异常
* @return the predecessor of this node
*/
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null) {
throw new NullPointerException();
}
else {
return p;
}
}
Node() {
}
/**
* addWaiter会使用此构造方法
* @param thread
* @param mode
*/
Node(Thread thread, Node mode) {
nextWaiter = mode;
this.thread = thread;
}
/**
* Condition会调用此函数
* @param thread
* @param waitStatus
*/
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
/**
* 状态分析:0和CONDITION为始态,CANCELLED为终态 0状态同时也可以是节点生命周期的终态
*/
}
三.属性定义
/**
* 逻辑上意义:当前持有锁的线程 本身并不会存储线程信息 延迟初始化
*/
private transient volatile Node head;
/**
* 当一个线程无法获取锁而被加入到同步队列中,会用CAS来设置尾结点tail为当前线的对应节点
*/
private transient volatile Node tail;
/**
* 同步状态 volatile:保证其他线程可见性 0 表示锁可获取状态
*/
private volatile int state;
/**
* 线程自旋的等待时间 微秒
*/
static final long spinForTimeoutThreshold = 1000L;
/**
* CAS操作
*/
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;
static {
try {
stateOffset = unsafe.objectFieldOffset(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
headOffset = unsafe.objectFieldOffset(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
tailOffset = unsafe.objectFieldOffset(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
waitStatusOffset = unsafe.objectFieldOffset(Node.class.getDeclaredField("waitStatus"));
nextOffset = unsafe.objectFieldOffset(Node.class.getDeclaredField("next"));
} catch (Exception ex) {
throw new Error(ex);
}
}
/**
* CAS设置头结点 只被enq()方法使用
*/
private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
/**
* CAS设置尾结点 仅被enq()方法使用
*/
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
/**
* CAS等待状态
*/
private static final boolean compareAndSetWaitStatus(Node node, int expect, int update) {
return unsafe.compareAndSwapInt(node, waitStatusOffset, expect, update);
}
/**
* CAS设置后继节点
*/
private static final boolean compareAndSetNext(Node node, Node expect, Node update) {
return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
}
四.独占式获取同步状态
1.模板方法acquire(int arg)
/**
* 独占式获取同步状态
* 首先执行tryAcquire()方法,尝试获取锁
* 获取失败 进入到addWaiter()方法,构造同步节点(独占式Node.EXCLUSIVE),将该节点添加到同步队列的尾部 并返回此节点 进入到acquireQueued()方法
* acquireQueued()方法 这个新节点以死循环的方式获取同步状态,若获取不到则阻塞节点中的线程,阻塞后的节点等待前驱节点来唤醒或阻塞线程被中断
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
selfInterrupt();
}
}
2.tryAcquire(int arg)
/**
* 独占式的获取锁 实现该方法需要查询当前状态并判断当前状态的是否符合预期,再进行CAS设置同步状态
*/
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
4.addWaiter(Node node)
/**
* 将构造的同步节点加入到同步队列中
* 使用链表的方式将Node节点添加到队列尾部,若tail的前驱节点不为空(队列不为空)则进行CAS添加到尾部
* 如果更新失败(存在并发竞争) 进入enq()方法进行更新
* @param mode Node.EXCLUSIVE 表示独占, Node.SHARED 表示共享
*/
private Node addWaiter(Node mode) {
//生成当前线程的节点
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
if (pred != null) {
//尾结点存在 将新生成的节点添加在尾结点tail后面
node.prev = pred;
//CAS操作将尾结点指向新生成的节点 之前尾结点的后继指向新生成的节点
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//尾结点不存在 表示同步队列还没有初始化 将新生成的节点添加到尾结点 通过自旋的方式进入队列
enq(node);
return node;
}
5.enq(Node node)
/**
* 以自旋的方式将节点添加到同步队列中
* 该方法使用自旋的方式来保证向队列中添加Node
* 若队列为空 ,则把当前Node设置成头结点
* 若队列不为空,泽祥队列尾部添加Node
* 该方式还是条件队列转换为同步队列的方法
*/
private Node enq(final Node node) {
//保证并发是能够正确的将node添加到队列尾端
for (; ; ) { // CAS自旋方式 - 死循环
Node t = tail;
//尾结点不存在
if (t == null) {
//CAS操作生成一个默认的节点作为尾节点
if (compareAndSetHead(new Node())) {
tail = head;
}
}
else {
//node节点的前驱节点指向尾结点
node.prev = t;
//CAS操作将尾结点指向node节点
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
6.acquireQueued(Node mode,int arg)
/**
* 以循环的方式获取同步状态 若获取不到则阻塞节点中的线程 阻塞后的节点等待前驱节点来唤醒或阻塞线程被中断
* 只有前驱节点是头结点,才能获取同步状态
*/
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (; ; ) {
//获取当前节点的前驱节点
final Node p = node.predecessor();
//检测P是否是头结点 若是 再次调用tryAcquire方法 尝试获取锁
if (p == head && tryAcquire(arg)) {
//是头结点 返回true(获取同步状态成功了) 将当前节点设置为头结点
setHead(node);
p.next = null; // 便于垃圾
failed = false;
return interrupted;
}
//若P节点不是头结点 或者tryAcquire返回false 获取失败
//判断node节点是否应该被阻塞 若应该被阻塞 那么阻塞node节点 并检测中断状态
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) {
//若又中断 设置中断状态
interrupted = true;
}
}
} finally {
if (failed) {
cancelAcquire(node);
}
}
}
7.shouldParkAfterFailedAcquire(Node pred, Node node)
/**
* 获取前驱节点的状态ws
* ws = SINGLE 表示可以被前驱节点唤醒 当前线程成可以被挂起 等待前驱节点唤醒 返回true
* ws>0 前驱节点取消 并循环检查此前驱节点之前所有连续取消的节点 并返回false(不能挂起)
* 尝试将当前节点的前驱节点的等待状态设置为SINGLE
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//获取前驱节点的状态
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
//若前驱节点状态为SINGLE 则当前线程可以被阻塞
{
return true;
}
if (ws > 0) {
//前驱节点的状态为CANCELLED 那就一直向前遍历 知道找到一个正常等待的状态
do {
node.prev = pred = pred.prev;
}
while (pred.waitStatus > 0);
//并将当前Node排在后面
pred.next = node;
}
else {
//前驱节点正常 CAS修改前驱节点的状态为SIGNAL
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
8.parkAndCheckInterrupt()
/**
* 检测线程是否被中断
*/
private final boolean parkAndCheckInterrupt() {
//阻塞当前线程
LockSupport.park(this);
//判断是否中断来唤醒的
return Thread.interrupted();
}
五.独占式释放锁
1.release(int arg)
/**
* 独占锁释放同步状态 释放同步状态之后 将同步队列中的第一个节点包含的线程唤醒
*/
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0) {
//唤醒后继线程
unparkSuccessor(h);
}
return true;
}
return false;
}
2.unparkSuccessor(Node node)
/**
* 使用LockSupport.unpark(Thread thread)来唤醒被阻塞的线程
* @param node the node
*/
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0) {
compareAndSetWaitStatus(node, ws, 0);
}
//获取后继线程
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev) {
if (t.waitStatus <= 0) {
s = t;
}
}
}
//唤醒后继线程
if (s != null) {
LockSupport.unpark(s.thread);
}
}
八.总结
在获取同步状态时,同步器维护一个同步队列,获取状态失败的线程会被加入到队列中并在队列中进行自旋,移除队列(或停止自旋)的前提条件是前驱节点为头结点,并且成功获取了同步状态。在释放同步状态时,同步器调用tryRelease(int arg)方法释放同步状态,然后唤醒节点的后继节点。