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);
}
}
解锁过程
public final boolean release(int arg) {
// 尝试解锁
if (tryRelease(arg)) {
// 解锁成功
Node h = head;
// 获取当前头部节点 若头部不为空 等待状态不为0 则进行唤醒
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
尝试解锁
protected final boolean tryRelease(int releases) {
// 由于可重试 每次解锁实际上是减少一次当前线程对锁的持有
int c = getState() - releases;
// 当前线程并不是锁的持有线程 异常情况 抛错
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
// 锁状态为0 则当前线程不再持有该锁 锁变成可上锁状态
free = true;
// 设置锁的持有者为空
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
唤醒头节点
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)
// CAS操作设置节点状态为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;
// 若该节点为空 或状态大于0 则表示该节点已取消
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);
}
参考资料:
https://blog.csdn.net/Java_zh...
https://blog.csdn.net/Leon_Ji...
https://blog.csdn.net/yy_dieg...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。