AbstractQueuedSynchronizer
最后看一下抽象类 AbstractQueuedSynchronizer,在同步组件的实现中,AQS 是核心部分,同步组件的实现者通过使用 AQS 提供的模板方法实现同步组件语义,AQS 则实现了对同步状态的管理,以及对阻塞线程进行排队,等待通知等等一些底层的实现处理。
AQS 的核心包括:同步队列,独占式锁的获取和释放,共享锁的获取和释放以及可中断锁,超时等待锁获取这些特性的实现
,而这些实际上则是 AQS 提供出来的模板方法。源码如下。
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer
implements java.io.Serializable {
/**
* 当共享资源被某个线程占有,其他请求该资源的线程将会阻塞,从而进入同步队列。
* 就数据结构而言,队列的实现方式无外乎两者一是通过数组的形式,另外一种则是链表的形式。
* AQS中的同步队列则是通过链式方式进行实现,下面的内部类Node便是其实现的载体
*/
static final class Node {
/** Marker to indicate a node is waiting in shared mode */
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode */
static final Node EXCLUSIVE = null;
// 节点从同步队列中取消
static final int CANCELLED = 1;
// 后继节点的线程处于等待状态,如果当前节点释放同步状态会通知后继节点,
// 使得后继节点的线程能够运行;
static final int SIGNAL = -1;
// 当前节点进入等待队列中
static final int CONDITION = -2;
// 表示下一次共享式同步状态获取将会无条件传播下去
static final int PROPAGATE = -3;
// 节点状态
volatile int waitStatus;
// 当前节点/线程的前驱节点
volatile Node prev;
// 当前节点/线程的后驱节点
volatile Node next;
// 加入同步队列的线程引用
volatile Thread thread;
// 等待队列中的下一个节点
Node nextWaiter;
final boolean isShared() {
return nextWaiter == SHARED;
}
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
Node() { // Used to establish initial head or SHARED marker
}
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
/**
* AQS实际上通过头尾指针来管理同步队列,同时实现包括获取锁失败的线程进行入队,
* 释放锁时对同步队列中的线程进行通知等核心方法。
*/
private transient volatile Node head;
private transient volatile Node tail;
/**
* 获取独占式锁
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
/**
* 释放独占式锁
*/
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
/**
* 获取可中断式锁
*/
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
/**
* 获取共享锁
*/
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
/**
* 释放共享锁
*/
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
}
J.U.C 中许多锁和并发工具类的核心实现都依赖于 AQS,如:ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch 等。
AQS 的源码中 方法很多,但主要做了三件事情:
1:管理 同步状态;
2 维护 同步队列;
3 阻塞和唤醒 线程
从行为上来区分就是 获取锁 和 释放锁,从模式上来区分就是 独占锁 和 共享锁。
实现原理
AQS 内部维护了一个 FIFO 队列来管理锁。线程首先会尝试获取锁,如果失败,则将当前线程以及等待状态等信息包成一个 Node 节点放入同步队列阻塞起来,当持有锁的线程释放锁时,就会唤醒队列中的后继线程。
伪代码
------------------------------- ### 获取锁
`·while (不满足获取锁的条件) {
把当前线程包装成节点插入同步队列
if (需要阻塞当前线程)
阻塞当前线程直至被唤醒
}
将当前线程从同步队列中移除`
--------------------------- ### 释放锁
修改同步状态
if (修改后的状态允许其他线程获取到锁)
唤醒后继线程