0 概述
详解文章
https://blog.csdn.net/TZ845195485/article/details/118517936
https://zhuanlan.zhihu.com/p/86072774
https://www.cnblogs.com/fsmly/p/11274572.html#_labelTop
https://blog.csdn.net/mulinsen77/article/details/84583716
AbstractQueueSynchronizer
- AQS是一个工具,juc的很多锁都是基于AQS实现的,如ReentrantLock,CountDownLatch,Semaphore;
- 核心变量 state,代表加锁的状态;不同的锁实现有不同的值;
- tryAcquire 加锁/获取资源 具体需要子类实现
- 加锁就是把Node节点里的thread设置为加锁线程,
state = state+arg
- 加锁失败的线程会构建一个节点,然后将节点加入到等待队列里(每次都挂在队列尾部)
- tryRelease 解锁/释放资源 具体需要子类实现
state = state-arg
- 等待队列的结点一直在自旋(for (; ; ; ))
- 等待占用资源的节点释放资源(队列头节点),不断尝试tryAcquire
英文
-
unpark 唤醒
-
park 阻塞
https://blog.csdn.net/weixin_39687783/article/details/85058686 -
successor 后继
-
predecessor 前驱
1 Node 节点的数据结构
volatile int waitStatus
;
- static final int CANCELLED = 1;
- static final int SIGNAL = -1; 节点处于此状态意味着后继节点将被唤醒
- static final int CONDITION = -2; 节点位于Condition队列
- static final int PROPAGATE = -3;
volatile Thread thread;
volatile Node prev;
volatile Node next;
Node nextWaiter;
2 资源的获取与释放
同步队列与等待队列图解
https://blog.csdn.net/MakeContral/article/details/78135531
/**
* Head of the wait queue, lazily initialized. Except for
* initialization, it is modified only via method setHead. Note:
* If head exists, its waitStatus is guaranteed not to be
* CANCELLED.
*/
private transient volatile Node head;
/**
* Tail of the wait queue, lazily initialized. Modified only via
* method enq to add new wait node.
*/
private transient volatile Node tail;
/**
* The synchronization state.
*/
//
private volatile int state;
2.1 访问资源 acquire
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
2.1.1 tryAcquire(arg)
tryAcquire方法返回一个布尔值,true表示当前线程能够访问资源,false当前线程不能访问资源
2.1.2 addWaiter(Node mode)
为当前线程构造一个节点,然后将节点加入到 wait Queue 的末尾处
/**
* Creates and enqueues node for current thread and given mode.
*
* @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
* @return the new node
*/
private Node addWaiter(Node mode) {
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) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//上一步失败了 用此方法
enq(node);
return node;
}
enq(final Node node)
/**
* Inserts node into queue, initializing if necessary. See picture above.
* @param node the node to insert
* @return node's predecessor
*/
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;
}
}
}
}
2.1.3 acquireQueued(final Node node, int arg) 阻塞
节点被加到到等待队列末尾处后,调用此方法使其进入阻塞状态
https://www.jianshu.com/p/dcbcea767d69
下面这段话来自上面的博客
/**
* Acquires in exclusive uninterruptible mode for thread already in
* queue. Used by condition wait methods as well as acquire.
*
* @param node the node
* @param arg the acquire argument
* @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();
// 如果前驱节点是头节点,并且获取锁成功,执行下面逻辑
// 队列第一个节点是当前正在处理的,tryAcquire(arg)获取锁成功说明前一个已经处理完了,所以轮到当前节点
if (p == head && tryAcquire(arg)) {
/* private void setHead(Node node) {
* head = node;
* node.thread = null;
* node.prev = null;
* }
*/
// 将head指针指向当前节点
setHead(node);
// 在sethead里 head已经不指向最初的头节点(也就是当前节点的前驱)
// 这里最初的头节点的后继指针也设置为null 那么这个节点彻底不被引用了
// 为了GC回收
p.next = null; // help GC
failed = false;
// 返回false表示不能被打断,意思是没有被挂起,也就是获得到了锁
return interrupted;
}
// 是否应该挂起(进入阻塞状态) 进入到这里说明获取锁失败 或者 p != head
if (shouldParkAfterFailedAcquire(p, node) &&
// 检查是否被中断,如果是返回true
/**
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
线程进入阻塞状态
return Thread.interrupted();
}
*/
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
shouldParkAfterFailedAcquire
为了确保当前节点阻塞后,能被安全唤醒,将此节点挂在 状态为signal 节点后边
/**
* Checks and updates status for a node that failed to acquire.
* 检查并且更新节点状态 如果获取锁失败
* Returns true if thread should block. This is the main signal
* 如果线程应该被阻塞返回true
* control in all acquire loops. Requires that pred == node.prev.
*
* @param pred node's predecessor holding status
* @param node the node
* @return {@code true} if thread should block
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
* 如果前驱节点的状态是 signal,可以安全的进行阻塞了(反正前驱节点会唤醒当前节点)
*
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
* 如果状态 > 0 ,前驱节点处于取消状态,跳过前驱节点
* 直到找到一个状态不大于0的
* 将node挂到那个节点后边
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
// 调用CAS 设置前驱结点的状态为signal
/*
* 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;
}
2.2 释放资源release
/**
* Releases in exclusive mode. Implemented by unblocking one or
* more threads if {@link #tryRelease} returns true.
* This method can be used to implement method {@link Lock#unlock}.
*
* @param arg the release argument. This value is conveyed to
* {@link #tryRelease} but is otherwise uninterpreted and
* can represent anything you like.
* @return the value returned from {@link #tryRelease}
*/
public final boolean release(int arg) {
// 尝试释放锁资源
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
unparkSuccessor
https://www.zhihu.com/question/50724462/answer/123776334
/**
* Wakes up node's successor, if one exists.
*
* @param node the node
*/
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;
// 如果状态码小于0 CAS设置状态码为0
if (ws < 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.
*/
// 获取后继节点
Node s = node.next;
// 如果后继节点为null 或者状态码>0(取消状态)
if (s == null || s.waitStatus > 0) {
// 重置新建节点
s = null;
// 从尾节点向前扫描
// 找到一个 状态码<=0的,并且不是当前节点 的 节点
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
// 如果到了当前节点或者头节点 不会进入循环
// t.prev != null 说明t不是head
}
// 唤醒这个节点
if (s != null)
LockSupport.unpark(s.thread);
}