0x00 概述
Java并发工具包JUC提供了许多并发工具,如常见的ReentrantLock可重入锁,Semaphore信号量等,它们都有一个共同的父类AbstractQueuedSynchronizer,AQS框架用来构建锁和同步器,该框架底层使用了CAS。
0x01 基本原理
- AQS类中持有一个volatile的变量state,用来表示同步状态。
- 图中三个方法用来操作同步状态。
其中,AQS支持两种同步方式,独占式和共享式
- 在这里额外说明一点,AbstractQueuedSynchronizer虽然被声明为abstract,但是类中没有任何一个抽象方法,声明为abstract仅仅是为了使得AQS方法无法被实例化,因为上图各个框中的方法默认实现都是直接抛出异常。
- 有人可能会异或,为什么要用继承而不用接口实现呢,不是说面向接口编程么,其实是框架的作者站在了开发者的角度来考虑的,如果采用接口的方式,那么开发者在实现自定义同步器的时候,比如要实现一个独占式的同步器,那么他也要实现共享式的方法,这样会给开发者带来疑惑,而采用继承的方式,加上模板方法的使用,就可以在子类中只选择重写需要的方法即可。
0x10 获取同步状态
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
以独占式acquire方法为例。主要步骤如下。
- 调用子类复写的tryAcquire方法,如果成功直接返回。否则执行步骤2.
- 否则,调用addWaiter方法将新增的EXCLUSIVE同步节点加入同步队列的尾部。
- 该节点在同步队列尝试获取同步状态,若获取不到,则阻塞结点线程,直到被前驱结点唤醒或者被中断。
private Node addWaiter(Node mode) {
//构建新节点,第一个参数表示nextWaiter,
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) {
//尾节点不为空,直接cas添加
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//尾节点为空,进入enq入队列方法
enq(node);
return node;
}
private Node enq(final Node node) {
//cas保证正确同步
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
//有可能多个线程进入enc方法,所以需要该步骤
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
回头看acquireQuequd请求同步队列方法
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();//p为node的前驱节点
if (p == head && tryAcquire(arg)) {//只有p为头节点才进行尝试获取同步方法
setHead(node);//获取同步状态成功,设置当前节点为头节点
p.next = null; // 显式置为null,GC发生时回收,(因为成功获得了同步状态,所以
p已经可被回收了)
failed = false;
return interrupted;
//在整个等待过程中被中断过,则返回true,否则返回false
}
//如果p不是头节点,或者没有获取到同步状态
if (shouldParkAfterFailedAcquire(p, node) &&//判断是否需要阻塞
parkAndCheckInterrupt())//进行阻塞
interrupted = true;
}
} finally {
if (failed)//如果失败,取消
cancelAcquire(node);
}
}