一、AQS是构建同步组件的基础
AQS以模板方式在内部定义了获取锁和释放同步状态的模板方法,并留下钩子函数供子类继承时进行扩展。钩子函数:tryAcquire(int arg)、tryRelease(int arg)、tryAcquireShared(int arg)、tryReleaseShared(int arg)
二、同步等待队列
AQS维护了一个同步等待队列,是一个双向链表;
//队列的头,懒加载,除了初始化,修改只有setHead方法,如果头存在,waitStatus不可能是CANCELLED
private transient volatile Node head;
//队列尾 懒加载,修改只有enq方法,增加新的节点
private transient volatile Node tail;
Node是AQS内部的一个静态内部类
/*下面是waitStatus*/
//表示线程等待超时或中断,取消继续等待
static final int CANCELLED = 1;
//表示线程释放锁后需要唤醒后续节点
static final int SIGNAL = -1;
//是不是使用condition的await
static final int CONDITION = -2;
//
static final int PROPAGATE = -3;
主要属性
//表示节点线程的状态
volatile int waitStatus;
//指向前置节点
volatile Node prev;
//后置节点
volatile Node next;
//当前节点的线程
volatile Thread thread;
Node nextWaiter;
三、独占模式源码解读
1、acquire(int i)
此方法是独占模式下线程获取锁的顶层入口;如果获取到锁,立即返回,否则进入等待队列;直到获取到资源为止,整个过程忽略中断
//获取独占锁
public final void acquire(int arg) {
//tryAcquire 是钩子函数需要子类实现,如果尝试获取失败,则加入等待队列
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
函数流程:
- tryAcquire尝试获取锁(需要子类自己实现),如果成功直接返回
- 如果失败,addWaiter会将该线程加入等待队列,并标记为独占模式
- acquireQueued()使线程在等待队列中获取资源,一直获取到资源后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false。
- 如果线程在等待过程中被中断,他是不响应的,只有在获取到资源的时候,才进行自我中断
2、tryAcquire(int)
tryAcquire()尝试获取锁,无论成功还是失败都会立即返回
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
AQS只是一个框架,具体看自定义同步器是如何实现,不使用抽象修饰,是因为对于独占只需实现tryAcquire-tryRelease,共享锁只需实现tryAcquireShared-tryReleaseShared
3、addWaiter(Node)
addWaiter方法用于将当前线程加入等待队列,并返回当前线程所在节点
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// 尝试快速放入队尾
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//上一步失败通过enq放入队尾
enq(node);
return node;
}
private Node enq(final Node node) {
//CAS加自旋,直到加入队尾
for (;;) {
Node t = tail;
if (t == null) { // tail=null表示队列还没有初始化,需要进行初始化操作
// 创建一个空节点为head并且tail也指向它
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
从以上加入队列可以看出,头结点为一个空节点
4、acquireQueued(Node,int)
当前节点是获取资源失败,已经放入等待队列;自旋获取锁,知道获取锁成功,并返回中断状态
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
//标记等待过程中是否被中断
boolean interrupted = false;
for (;;) {
//获取前置节点
final Node p = node.predecessor();
//前置节点为head的时候,才可以尝试获取资源
//(head为空节点,此时当前节点为实际头结点)
if (p == head && tryAcquire(arg)) {
//获取锁成功后, 将当前节点设置为头结点,并修改为空节点
setHead(node);
// 原来的头结点移除引用
p.next = null; // help GC
failed = false;
return interrupted;
}
//是否需要进入waiting状态,知道被unpark
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
//当子类实现的tryAcquire方法抛出异常时,取消锁获取
cancelAcquire(node);
}
}
4.1、shouldParkAfterFailedAcquire
这个是很重要的方法,获取锁失败后寻找安全点休眠
如果前置节点正常,则返回true,进入parkAndCheckInterrupt休眠;否则再回到acquireQueued方法中的自旋直至获取锁或者异常或者返回true
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.
*/
//指后续节点需要park
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
// 前置节点取消了,就继续向前找,直到找到最近的一个正常状态,并放在它后面;
// 那些被放弃的节点,由于自己的加塞,他们户会形成一个无用的链,GC会回收
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} 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.
*/
//如果前置节点正常,就设置状态为SIGNAL,当前也可能失败
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
4.2、parkAndCheckInterrupt()
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);//调用park使线程进入waiting
return Thread.interrupted();//唤醒后返回中断状态
}
acquireQueued()流程
- 节点进入队尾后检查状态,前置节点是否为head,是否能获取资源,不能获取就找到安全点waiting
- 调用park()进入waiting,等待unpark或interrupt唤醒
- 被唤醒后查看自己是否有资格获取资源,如果拿到,head指向当前节点,返回等待过程中是否被中断的标志;如果没有拿到,1重新开始
5、release(int)
独占模式下释放资源的顶层入口;彻底释放后,负责唤醒等待队列里的其他线程获取资源
public final boolean release(int arg) {
// 自定义实现的释放方法
if (tryRelease(arg)) {
Node h = head;
// 如果头结点为null说明没有等待队列,无需唤醒;
// 如果头结点的状态为0,说明头节点初始化后,第二位节点正在寻找安全位置时,无需唤醒
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);// 唤醒等待队列的下一个节点
return true;
}
return false;
}
5.1、unparkSuccessor(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;
if (ws < 0) // 设置头结点status为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;
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);
}
四、共享模式源码解读
1、acquireShared(int)
1、1 doAcquireShared(int)
获取共享锁的主要方法:
加入队列并返回节点,如果当前节点的前置节点为head便尝试获取锁,如果不是则寻找安全位置休眠
private void doAcquireShared(int arg) {
//addWaiter方法用于将当前线程加入等待队列,并返回当前线程所在节点
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//获取前置节点
final Node p = node.predecessor();
if (p == head) {
//前置节点为head,表示有资格去尝试获取锁
int r = tryAcquireShared(arg);
if (r >= 0) {
//>=0表示获取锁成功
//设置当前节点为头节点,并且向后传播
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
//如果在等待过程中断过,在这将中断补上
selfInterrupt();
failed = false;
return;
}
}
//与独占锁一致,寻找安全点waiting,等待被唤醒或者中断
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
1.2 setHeadAndPropagate(Node,int)
// 共享锁获取成功后设置头结点,标记
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);//设置当前节点为头节点
//如果还有剩余量,则唤醒下一个共享线程
// 不论是老的头结点还是新的只要waitStatus <0 都会唤醒后继节点
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
// 如果当前节点的后继节点是共享类型或者没有后继节点,则进行唤醒
if (s == null || s.isShared())
doReleaseShared();
}
}
1.3 doReleaseShared()
private void doReleaseShared() {
for (;;) {
Node h = head;
// 对于队列存在元素才进行唤醒
if (h != null && h != tail) {
int ws = h.waitStatus;
//表示后继节点需要被唤醒
if (ws == Node.SIGNAL) {
//设置头结点状态为0,防止唤醒两次
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);//唤醒下一个节点
}
//如果后继节点暂时不需要唤醒,则把当前节点状态设置为PROPAGATE确保以后可以传递下去
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
// 如果头结点没有变化则结束
// 如果有其他线程获取了锁,则需要自旋继续唤醒
if (h == head) // loop if head changed
break;
}
}
五、公用源码
1、cancelAcquire(Node)
取消继续获取锁
private void cancelAcquire(Node node) {
// Ignore if node doesn't exist
if (node == null)
return;
node.thread = null;
// 跳过已取消的前置任务,找到正常前置节点
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
// 正常前置节点的后置节点,以便使用CAS修改
Node predNext = pred.next;
// 设置当前节点的状态为1,其他操作会忽略当前节点
node.waitStatus = Node.CANCELLED;
// 如果当前节点是尾节点,那么前置正常节点需要成为尾节点
if (node == tail && compareAndSetTail(node, pred)) {
// 成为尾节点,需要将其后置节点设置为null
compareAndSetNext(pred, predNext, null);
} else {
// If successor needs signal, try to set pred's next-link
// so it will get one. Otherwise wake it up to propagate.
int ws;
// 如果前置不是头结点,并且状态不是SIGNAL 设置为SIGNAL,持有线程
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
//将前置节点的后置节点设置为当前节点的后置节点
compareAndSetNext(pred, predNext, next);
} else {
unparkSuccessor(node);
}
node.next = node; // help GC
}
}
六、ConditionObject
基础属性
/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;
1、await()
此方法是响应中断的,调用此方法会释放当前获取的锁,直到被唤醒
添加一个等待者进入队列
//由于是独占锁已被获取,所以不会出现线程安全问题
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//加入队列
Node node = addConditionWaiter();
//释放当前线程的锁
int savedState = fullyRelease(node);
int interruptMode = 0;
// 判断节点是否在同步队列中
while (!isOnSyncQueue(node)) {
LockSupport.park(this);//等待
//等待被唤醒后,检查中断,并尝试降入同步队列
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// acquireQueued来尝试获取锁,或寻找安全点
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
// 是自我中断还是抛出中断异常
reportInterruptAfterWait(interruptMode);
}
1、1addConditionWaiter()
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
//移除被取消的waiter
unlinkCancelledWaiters();
t = lastWaiter;
}
// 为当前线程创建一个节点
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
1.1.1、unlinkCancelledWaiters
private void unlinkCancelledWaiters() {
Node t = firstWaiter;
//用于临时存放遍历到的正常节点,以便指向下一个正常节点
Node trail = null;
while (t != null) {
//获取下一个节点
Node next = t.nextWaiter;
if (t.waitStatus != Node.CONDITION) {
t.nextWaiter = null;
if (trail == null)
//之前没有正常节点,首节点就是当前节点的next
firstWaiter = next;
else
// 存在正常节点,它的next指向,当前废弃节点的next
trail.nextWaiter = next;
if (next == null)
//如果next为null,trail可能是正常节点也可能是null
lastWaiter = trail;
}
else
trail = t;
t = next;
}
}
1.2、fullyRelease
释放当前线程获取的锁
final int fullyRelease(Node node) {
boolean failed = true;
try {
int savedState = getState();
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
1.3、isOnSyncQueue(node)
final boolean isOnSyncQueue(Node node) {
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
if (node.next != null) // If has successor, it must be on queue
return true;
//在同步队列中查找当前节点
return findNodeFromTail(node);
}
private boolean findNodeFromTail(Node node) {
Node t = tail;
for (;;) {
if (t == node)
return true;
if (t == null)
return false;
t = t.prev;
}
}
1.4
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
final boolean transferAfterCancelledWait(Node node) {
// 如果没有被中断就尝试修改状态为0
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
//加入同步队列
enq(node);
return true;
}
while (!isOnSyncQueue(node))
Thread.yield();
return false;
}
2、signal
唤醒await中的线程
public final void signal() {
if (!isHeldExclusively())//不是持有锁的线程抛出异常
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
//唤醒头节点
doSignal(first);
}
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
// 将节点加入同步队列
Node p = enq(node);
int ws = p.waitStatus;
//如果节点被取消或者设置SIGNAL状态失败,就唤醒进行重新同步
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);
first = next;
} while (first != null);
}