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…
发表回复