共计 3340 个字符,预计需要花费 9 分钟才能阅读完成。
AQS
java.util.concurrent.locks.AbstractQueuedSynchronizer,译为形象队列式同步器
AQS 提供了原子式治理同步状态、阻塞和唤醒线程性能以及期待队列模型的简略框架;
AQS 蕴含了一个虚构的 Node 双向链表(即期待队列),由 volatile 润饰的头 & 尾节点,以及同步状态标记 state,节点期待状态标记 waitStatus,和以后线程信息
AQS 提供了共享锁与独占锁的反对
独占锁
同一个时刻只能被一个线程占有,如 ReentrantLock,ReentrantWriteLock 等,其中又蕴含了
偏心锁与非偏心锁
共享锁
同一时间点能够被多个线程同时占有,如 ReentrantReadLock,Semaphore 等
偏心锁 & 非偏心锁
初始化
// 偏心锁
ReentrantLock lock = new ReentrantLock(true);
// 非偏心锁
ReentrantLock lock = new ReentrantLock();
ReentrantLock lock = new ReentrantLock(false);
加锁过程
java.util.concurrent.locks.ReentrantLock.FairSync#lock
final void lock() {
// 尝试将以后锁的状态置为 1 即为加锁胜利
acquire(1);
}
public final void acquire(int arg) {
/**
* 尝试加锁 tryAcquire = true 则加锁胜利
* 加锁失败时进入后续的期待流程 acquireQueued
* acquireQueued 将有限循环获取锁
* 若获取到锁间接开始执行
* 或前节点成为头部 本人行将执行 则临时挂起
*/
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();}
1. 尝试加锁(偏心锁模式)
/**
* 尝试获取锁 设置锁状态 锁状态的数值理论为获取锁线程的加锁的次数(可重入)* 返回 true 为加锁胜利 false 为失败
*/
protected final boolean tryAcquire(int acquires) {
// 以后线程
final Thread current = Thread.currentThread();
// 获取以后锁的状态
int c = getState();
// 以后锁状态为 0 则为能够加锁
if (c == 0) {
/**
* 非偏心锁模式将不会判断队列是否有其余节点 会间接尝试获取锁
* hasQueuedPredecessors = false
* 则期待队列中没有其余线程
* 即以后线程是下一个该当获取锁的线程
*/
if (!hasQueuedPredecessors() &&
/**
* 思考到有其余线程同时判断结束
* 故进行 CAS 操作 尝试批改锁状态
*/
compareAndSetState(0, acquires)) {
// 前两步都已胜利 则将以后线程设置为持有锁的线程
setExclusiveOwnerThread(current);
return true;
}
}
// 以后锁状态不为 0 且持有锁的线程是以后线程 则间接重入
else if (current == getExclusiveOwnerThread()) {
// 设置以后锁状态 +1 即以后线程总共上锁的次数
int nextc = c + acquires;
// 若上锁次数为负 实为上锁次数越界
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
判断是否期待队列中是否有其余线程(非偏心锁没有此步骤)
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
/**
* 队列头部 与 队列尾部元素 不相等
* 且头部后一个元素不为空 或头部后一个期待线程不是以后线程
* 阐明队列中至多有其余的线程在期待
*/
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
CAS 操作进行锁状态变更
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
2. 加锁失败 进入期待队列
/**
* 将以后线程构建为期待队列的元素 即 Node
* 下文将用 Node 示意以后线程构筑的期待队列元素
* 将该 Node 期待的线程设置为以后锁的持有线程
* CAS 操作 尝试将以后 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 的前节点元素
node.prev = pred;
// 尝试将以后线程 Node 设置成尾部元素
if (compareAndSetTail(pred, node)) {
// 胜利后 批改旧的队尾元素的后一个节点为以后 Node
pred.next = node;
return node;
}
}
enq(node);
return node;
}
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
// 以后 Node 会在此处有限循环 直到前节点成为头部 本人获取到锁才返回
for (;;) {
// 获取以后 Node 的前节点
final Node p = node.predecessor();
// 若以后节点的前节点已成为队列头部 则再次尝试加锁
if (p == head && tryAcquire(arg)) {
// 胜利后 设置以后 Node 为头部元素
setHead(node);
p.next = null; // help GC
// 此时已获取到锁 故无需再设置本人为中断状态
// 进入队列后果则为失败
failed = false;
return interrupted;
}
/**
* shouldParkAfterFailedAcquire 判断以后线程是否可能挂起
* 革除队列中已勾销的节点
* 判断为 true 则能够挂起
*/
if (shouldParkAfterFailedAcquire(p, node) &&
/**
* parkAndCheckInterrupt 执行挂起
* 并返回以后线程是否有过中断请求
*/
parkAndCheckInterrupt())
/**
* 以后线程在期待过程中无奈响应中断 直到获取到锁
* 如果在整个期待过程中被中断过 则 interrupted = true
* acquireQueued 最终返回 true 否则返回 false
* 并在外层响应中断
* 须要其余线程调用以后线程的 interrupt()办法
*/
interrupted = true;
}
} finally {if (failed)
cancelAcquire(node);
}
}
参考资料:
https://blog.csdn.net/Java_zh…
https://blog.csdn.net/Leon_Ji…
https://blog.csdn.net/yy_dieg…
正文完