定义
AbstractQueuedSynchronizer为实现依赖于先进先出 (FIFO) 等待队列的阻塞锁定和相关同步器(ReentrantLock、Semaphore,CountDownLatch等等)提供一个框架。此类的设计目标是成为依靠单个原子 int 值来表示状态的大多数同步器的一个有用基础。子类必须定义更改此状态的受保护方法,并定义哪种状态对于此对象意味着被获取或被释放。假定这些条件之后,此类中的其他方法就可以实现所有排队和阻塞机制。但只是为了获得同步而只追踪使用 getState()、setState(int) 和 compareAndSetState(int, int) 方法来操作以原子方式更新的 int 值。
数据结构
AbstractQueuedSynchronizer是“CLH”(Craig、Landin和Hagersten)锁队列的变体。CLH锁通常用于旋转锁。我们使用它们来实现同步器,在其节点的前一个线程中保存一些控制信息。每个节点中的“status”属性判断线程是否应该阻塞。节点在其前置节点释放时被唤醒。队列的每个节点都充当一个特定的通知监视器,其中包含一个等待线程。但是,status属性不控制线程是否被授予锁等。如果线程是队列中的第一个线程,它可能会尝试获取。但是,第一并不能保证成功,它只赋予了竞争的权利。所以当前释放的竞争者线程可能需要重新等待。
要加入CLH锁,你可以自动将其作为新尾部拼接。 要出队,你只需设置头字段。
内部类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;
/**
* 表示线程正在等待某一个条件
*/
static final int CONDITION = -2;
/**
* share模式下使用
*/
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;
}
}
方法解析
/**
* 实现lock方式的的主要方法
* 1、tryAcquire由子类自己实现,主要用来尝试获取锁
* 2、addWaiter:如果队列为空就初始化队列,否则就把节点
* 加到队列尾部
* 3、acquireQueued:以无限循环的方式获取锁,如果获取不到就
* 阻塞当前线程。
* 4、selfInterrupt:中断当前线程
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
ReentrantLock中的tryAcquire
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//state值,一般0表示未获取,1表示已经获取了,大于1表示重入锁的个数。
int c = getState();
if (c == 0) {
//以CAS的方式设置state
if (compareAndSetState(0, acquires)) {
//设置当前线程为独占
setExclusiveOwnerThread(current);
return true;
}
}
//如果当前线程是当前独占锁的线程,对可重入锁的支持
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//设置stare值
setState(nextc);
return true;
}
return false;
}
addWaiter
private Node addWaiter(Node mode) {
//当前线程封装成一个node节点
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
//快速尝试一次直接入队尾,如果失败再无限循环尝试
Node pred = tail;
//如果队尾节点不为null
if (pred != null) {
//原队尾节点为当前节点的前一个节点
node.prev = pred;
//以cas的设置当前节点为尾节点
if (compareAndSetTail(pred, node)) {
//原队尾节点的next节点为当前节点
pred.next = node;
return node;
}
}
//如果快速入队尾失败,则通过enq方式入队
enq(node);
return node;
}
//和快速尝试方式一样设置尾节点,只是多了一次判断是否需要初始化
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
//初始化队列
if (compareAndSetHead(new Node()))
tail = head;
} else {
//设置队尾节点
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
acquireQueued
final boolean acquireQueued(final Node node, int arg) {
//获取锁是否失败
boolean failed = true;
try {
//等待锁时是否被中断
boolean interrupted = false;
for (;;) {
//获取前一个节点
final Node p = node.predecessor();
//判断前一个节点是否是头节点,如果是则再次尝试获取一次锁
if (p == head && tryAcquire(arg)) {
//设置当前节点为头节点
setHead(node);
p.next = null; // help GC
//表示获取锁成功
failed = false;
//表示未被中断
return interrupted;
}
//如果前一个节点不是头节点,或者是头结点但是获取锁失败,则判断是否需要阻塞当前节点
if (shouldParkAfterFailedAcquire(p, node) &&
//阻塞当前线程,并返回中断状态
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
shouldParkAfterFailedAcquire
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
//如果前一个节点waitStatus==SIGNAL,直接返回true,即为需要被阻塞
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
//如果waitStatus大于0,即waitStatus==CANCELLED
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
//一直遍历查找,直到找到前一个节点状态不为CANCELLED为止
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
//找到了合适的节点,将当前节点设置为前一个节点的下一个节点
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
//前一个节点的waitStatus为0或者PROPAGATE,即需要等待一个singal,但是不阻塞,调用非需要重试以确保阻塞前无法获取
//cas的方式设置前一个节点的waitStatus值为SIGNAL
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
parkAndCheckInterrupt
private final boolean parkAndCheckInterrupt() {
//阻塞线程
LockSupport.park(this);
//返回线程中断状态
return Thread.interrupted();
}
获取锁的流程大致如此,接下来看一个释放锁的流程。
release释放锁方法的入口
public final boolean release(int arg) {
//由子类自己实现,一般就通过cas的方式修改state的值
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//唤醒后续节点的线程
unparkSuccessor(h);
return true;
}
return false;
}
ReentrantLock中tryRelease的实现方式
/**
*和tryAcquire方式基本一样,一个加一个减
*/
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
unparkSuccessor
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
//节点状态为signal,尝试清除signal状态,以cas方式设置waitStatus为0
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
//从尾向头遍历,找到第一个当前节点的下一个节点不为cancelled的。
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);
}
总结
在获取同步状态时,同步器维护一个同步队列,tryAcquire失败的线程都会被加入到队列中并在队列中进行阻塞并且自旋;移出队列(或停止自旋)的条件是前驱节点为头节点且tryAcquire成功。在释放同步状态时,同步器调用tryRelease(int arg)方法释放同步状态,然后唤醒head指向节点的后继节点。