https://ifeve.com/introduce-abstractqueuedsynchronizer/
AbstractQueuedSynchronizer 继承了AbstractOwnableSynchronizer
AbstractOwnableSynchronizer 辅助实现了线程独占锁
AbstractQueuedSynchronizer 属性:
// 队列头结点
private transient volatile Node head;
// 队列尾结点
private transient volatile Node tail;
// 同步计数器
private volatile int state;
protected final int getState() {
return state;
}
protected final void setState(int newState) {
state = newState;
}
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
// 下面属性用于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); }
}
静态内部类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;
// 状态需要向后传播
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;
}
// 返回前驱节点 如无前驱节点 NPE
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;
}
}
acquire 方法: final 方法 子类不可重写 也没重写需求的方法
代码块
public final void acquire(int arg) {
// 子类重写tryAcquire和tryRelease方法通过CAS指令修改状态变量state。
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
线程A和B进行竞争。
-
线程A执行CAS执行成功,state值被修改并返回true,线程A继续执行。
-
线程A执行CAS指令失败,说明线程B也在执行CAS指令且成功,这种情况下线程A会执行步骤3。
-
生成新Node节点node,并通过CAS指令插入到等待队列的队尾(同一时刻可能会有多个Node节点插入到等待队列中),
如果tail节点为空,则将head节点指向一个空节点(代表线程B),具体实现如下:
tryAcquire 方法 父类方法直接抛异常 必须子类重写的方法
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
addWaiter 方法: 静态内部类Node实现的方法
代码块
private Node addWaiter(Node mode) {
// 创建新Node节点 并初始化 1 nextWaiter 为独占节点 2 thread 为当前线程
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
// 通过cas 尝试快速将节点插入到队尾中
Node pred = tail;
// 如果队尾不为null
if (pred != null) {
// 将当前节点的前驱设置为尾巴节点
node.prev = pred;
// CAS 将当前节点设置为尾巴节点 旧数据为 旧尾巴节点 新数据为 当前节点
if (compareAndSetTail(pred, node)) {
// 更新原尾巴节点的 后继为当前节点
pred.next = node;
// 返回当前节点
return node;
}
}
// 如上述cas 更新队尾实现入队失败 执行enq入队
enq(node);
// 返回当前节点
return node;
}
private Node enq(final Node node) {
for (;;) {
// 获取当前尾巴节点
Node t = tail;
// 如尾巴节点为null cas将伪头结点 设置为队头
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
// 设置队头成功 将尾巴节点设置为 头结点 同步队列的头尾节点都是伪节点 继续下一次循环
tail = head;
} else {
// 将当前节点的前驱 设置为 尾巴节点
node.prev = t;
// CAS 尝试将尾巴节点设置为 当前节点
if (compareAndSetTail(t, node)) {
// 尾巴节点的后继 设置为当前节点
t.next = node;
// 返回当前节点的前驱节点
return t;
}
}
}
}
acquireQueued 方法 头结点所对应的含义是当前占有锁且正在运行 如为伪头结点 则说明之前队列未初始化 伪头结点也标识 当前占有锁的线程
代码块
// return {@code true} if interrupted while waiting
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 获得当前节点的前驱节点
final Node p = node.predecessor();
// 如前驱节点为头结点 则说明当前节点 为真正的头结点 再次尝试cas 修改同步计数器
if (p == head && tryAcquire(arg)) {
// 将当前节点设置为头结点 标识资源已被头结点所占有
setHead(node);
p.next = null; // help GC
failed = false; // 失败标识为false
return interrupted; // 返回未被中断状态
}
// 上面cas更改state失败 挂起当前线程
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
在node的插入过程,线程B(即之前没有阻塞的线程)可能已经执行完成,
所以要判断该node的前一个节点pred是否为head节点(代表线程B),
如果pred == head,表明当前节点是队列中第一个“有效的”节点,因此再次尝试tryAcquire获取锁
有效: 等待锁的节点
shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()
shouldParkAfterFailedAcquire方法
代码块
// return {@code true} if thread should block
// 判断当前节点对应线程是否可以挂起
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
// pred的waitStatus为Node.SIGNAL,则通过LockSupport.park()方法把线程A挂起,并等待被唤醒
if (ws == Node.SIGNAL)
return true;
// 如果前驱节点是CANCELLED(1),跳过该节点,向前找到状态不是CANCELLED的那个节点作为当前节点的前驱。 当前线程不挂起再重试
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
// 如果pred的waitStatus == 0 或者为向后传播状态,则通过CAS指令修改前驱节点状态 waitStatus为Node.SIGNAL 当前线程不挂起再重试
} 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.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
// 当前线程不挂起再重试
return false;
}
如果上面方法返回true 进入下面方法:
parkAndCheckInterrupt方法
代码块
private final boolean parkAndCheckInterrupt() {
// 执行挂起
LockSupport.park(this);
return Thread.interrupted();
}
挂起后再唤醒之后的操作:
线程每次被唤醒时,都要进行中断检测,如果发现当前线程被中断,那么抛出InterruptedException并退出循环。
从无限循环的代码可以看出,并不是被唤醒的线程一定能获得锁,必须调用tryAccquire重新竞争,因为锁是非公平的,
有可能被新加入的线程获得,从而导致刚被唤醒的线程再次被阻塞,这个细节充分体现了“非公平”的精髓。
线程释放锁过程:
release方法
代码块
public final boolean release(int arg) {
// 如尝试释放锁成功
if (tryRelease(arg)) {
// 拿到头结点
Node h = head;
// 如头结点不为null 且 等待状态 已初始化了
if (h != null && h.waitStatus != 0)
// 唤醒后继节点
unparkSuccessor(h);
return true;
}
return false;
}
唤醒节点:
unparkSuccessor 方法:
代码块
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
// 如头结点状态 不为取消和未初始化状态
if (ws < 0)
// 将头结点状态重置为0
compareAndSetWaitStatus(node, ws, 0);
// 获得当前节点的后继节点
Node s = node.next;
// 如后继节点为null 或 为取消状态
if (s == null || s.waitStatus > 0) {
s = null;
// 从尾巴节点 向前遍历 拿到最靠前面的非取消状态有效节点 直到 节点为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);
}