经过了前两篇AbstractQueuedSynchronizer源码分析- ReentrantLock抢锁解锁, AbstractQueuedSynchronizer源码分析- CountDownLatch分析,我么对AQS已经学习了差不多了, 但是还有一些细节我们没有进行分析, 如ReentrantLock 公平锁和非公平锁的区别, AQS如何绑定Condition,实现条件唤醒, 线程中断的细节, AQS无处不在的InterruptedException.下面我们主要围绕以下几点进行分析
1. 深入理解 ReentrantLock 公平锁和非公平锁的区别
2. 深入分析 AbstractQueuedSynchronizer 中的 Condition
3. 了解 Java 线程中断
4. 了解InterruptedException 异常
公平锁与非公平锁
众所周知synchronizer与ReentrantLock有一个很大的区别就是synchronizer关键字是非公平锁, 而ReentrantLock可实现公平锁与非公平锁,根据前两篇文章的学习, 我们不妨先不进行源码分析, 先想一下,如果是你设置ReentrantLock公平锁与非公平锁, 你会怎样设计.
创建
ReentrantLock 默认采用非公平锁,除非你在构造方法中传入参数 true 。
public ReentrantLock() {
// 默认非公平锁
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
加锁
// 公平锁
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 1. 和非公平锁相比,这里多了一个判断:是否有线程在等待
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 非公平锁
final void lock() {
// 2. 和公平锁相比,这里会直接先进行一次CAS,成功就返回了
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
分析
- 非公平锁抢锁,先CAS 抢锁,成功返回true, 失败才进行acquire(1)操作.
- 非公平锁在 CAS 失败后,进入tryAcquire(1), 如果锁这个时候被释放了(state == 0),非公平锁会直接 CAS 抢锁,但是公平锁会判断队列是否有线程处于等待状态,如果有则不去抢锁,进行入队操作.
- 非公平锁的优点是性能比公平锁好, 吞吐量增加, 缺点是如果一直有新的线程进行抢锁可能会导致队列中的线程一直处于别唤醒状态.
Condition代码演示
// 资源类, 绑定Condition, 实现条件唤醒
public class ConditionResource {
private int number = 1;
ReentrantLock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
public void printA() {
try {
lock.lock();
while (number != 1) {
condition1.await();
}
System.out.println(Thread.currentThread().getName() + "工作, 下一个B");
number = 2;
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB() {
try {
lock.lock();
while (number != 2) {
condition2.await();
}
System.out.println(Thread.currentThread().getName() + "工作, 下一个C");
number = 3;
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() {
try {
lock.lock();
while (number != 3) {
condition3.await();
}
System.out.println(Thread.currentThread().getName() + "工作, 下一个A");
number = 1;
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
// 测试类, 创建三个线程, 对上面资源类各打印5次
@Test
public void testCondition() throws InterruptedException {
ConditionResource resource = new ConditionResource();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
resource.printA();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
resource.printB();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
resource.printC();
}
}, "C").start();
}
Condition分析之前, 我们先引入一个新的概念, 条件队列. Condition创建就是创建一个条件队列
Condition创建
// lock.newCondition() 实际上是创建ConditionObject对象
public Condition newCondition() {
return sync.newCondition();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
ConditionObject属性
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
// 条件队列的第一个节点
private transient Node firstWaiter;
// 条件队列的最后一个节点
private transient Node lastWaiter;
......
}
await操作
public final void await() throws InterruptedExczeption {
if (Thread.interrupted())
throw new InterruptedException();
// 1. 添加到 condition 的条件队列中
Node node = addConditionWaiter();
// 2. 完全释放独占锁
int savedState = fullyRelease(node);
int interruptMode = 0;
// 3. 将node转移到阻塞队列, 返回true, 转移成功, 返回false
while (!isOnSyncQueue(node)) {
// 线程挂起
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 被唤醒后,将进入阻塞队列,等待获取锁
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
//1, 返回值为lastWaiter
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
// 如果lastWaiter被取消,请清理,
// (Node.CONDITION = -2)取消排队
if (t != null && t.waitStatus != Node.CONDITION) {
// 1.1 清除取消排队的WaitStatus
unlinkCancelledWaiters();
t = lastWaiter;
}
// 创建node,waitStatus指定为Node.CONDITION = -2
Node node = new Node(Thread.currentThread(), Node.CONDITION);
// 条件队列的最后一个节点为null, 标示队列为空
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
// 1.1
// 从条件队列队头开始遍历链表,若 t.waitStatus = -2, t的上一个节点的下一个节点指向t的下一个节点
//t.waitStatus != -2, t上一个节点的下一个节点指向t节点, 若t为null, t上一个节点设值为条件队列最后一个节点
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)
firstWaiter = next;
else
trail.nextWaiter = next;
if (next == null)
lastWaiter = trail;
}
else
trail = t;
t = next;
}
}
// 2. 完全释放独占锁
final int fullyRelease(Node node) {
boolean failed = true;
try {
int savedState = getState();
// 2.1 唤醒头节点的下一个
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
// 若执行到这里, 标示唤醒下一个节点失败, 或者异常, 将这个节点的waitStatus 设置1,取消排队
node.waitStatus = Node.CANCELLED;
}
}
// 2.1
public final boolean release(int arg) {
// 2.1.1
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
// 2.1.2 唤醒阻塞队列中头节点的下一个节点线程
unparkSuccessor(h);
return true;
}
return false;
}
//2.1.1 设置当前线程为null, 将state设置为0, 返回true
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);// 设置state为0, 完全释放独占锁
return true;
}
// 3.判断是否已经转移到阻塞队列
final boolean isOnSyncQueue(Node node) {
// 3.1 移动过去的时候,node 的 waitStatus 会置为 0,这个之后在说 signal 方法的时候会说到
// 如果 waitStatus 还是 Node.CONDITION,也就是 -2,那肯定就是还在条件队列中
// 如果 node 的前驱 prev 指向还是 null,说明肯定没有在阻塞队列(prev是阻塞队列链表中使用的)
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
// 3.2 如果 node 已经有后继节点 next 的时候,那肯定是在阻塞队列了
if (node.next != null)
return true;
// 3.3 从阻塞队列的队尾往前遍历,如果找到,返回 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;
}
}
signal操作
public final void signal() {
// 1,如果state == 0 抛异常, 调用 signal 方法的线程必须持有当前的独占锁
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
// 2, 执行唤醒
doSignal(first);
}
// 1
protected boolean isHeldExclusively() {
return getState() != 0;
}
// 2 从条件队列队头往后遍历,找出第一个需要转移的 node
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) {
// 将 waitStatus 置为 0
// CAS 如果失败,说明此 node 的 waitStatus 已不是 Node.CONDITION,说明节点已经取消,转移后面一个节点
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
// 自旋进入阻塞队列的队尾, 之前有分析
// p是node的前驱节点
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
// 前驱节点ws > 0, 取消排队, 或者设置ws为-1失败唤醒node节点线程
LockSupport.unpark(node.thread);
return true;
}
signal操作是唤醒线程, 转移到阻塞队列, 线程转移到阻塞队列之后, 当前线程执行完毕, 释放锁之后, 会唤醒调用signal的线程.开始执行await操作中LockSupport.park(this);后面的程序.
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
// 接着这里往下执行
// 1 等待时检查中断
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break;
}
// 2. 这个方法返回的时候,代表当前线程获取了锁,而且 state == savedState了 && interruptMode != THROW_IE,说明在 signal 之前就发生中断了,这里将 interruptMode 设置为 REINTERRUPT,用于待会重新中断
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
// 3. 因为可能存在signal前发生的中断, 需要清理数据
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
// 4.
// 0:什么都不做,没有被中断过;
// THROW_IE:await 方法抛出 InterruptedException 异常,因为它代表在 await() 期间发生了中断;
// REINTERRUPT:重新中断当前线程,因为它代表 await() 期间没有被中断,而是 signal() 以后发生的中断
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
// 等待时检查中断,
private int checkInterruptWhileWaiting(Node node) {
// 1.1 Thread.interrupted() 如果当前线程已经处于中断状态,那么该方法返回 true,同时将中断状态重置为 false
// REINTERRUPT: 代表 await 返回的时候,需要重新设置中断状态
//THROW_IE: 代表 await 返回的时候,需要抛出 InterruptedException 异常
return Thread.interrupted() ?
// 1.2 结果为true, 线程在挂起的时候被中断过, 执行transferAfterCancelledWait(node)
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
// 1.2
// 在signa前发生的中断, 返回true, 此时interruptMode = THROW_IE
// 在signa后发生的中断, 返回false, 此时interruptMode = REINTERRUPT
final boolean transferAfterCancelledWait(Node node) {
// 用 CAS 将节点状态设置为 0
// 如果这步 CAS 成功,说明是 signal 前发生的中断,因为如果 signal 先发生的话,signal 中会将 waitStatus 设置为 0
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
// 设置成功, 转移到阻塞队列
enq(node);
return true;
}
// 到这里是因为 signal 方法已经将 waitStatus 设置为了 0
// signal 方法会将节点转移到阻塞队列,但是可能还没完成,这边自旋等待其完成
// 当然,这种事情还是比较少的吧:signal 调用之后,没完成转移之前,发生了中断
while (!isOnSyncQueue(node))
Thread.yield();
return false;
}
// 4.
private void reportInterruptAfterWait(int interruptMode)
throws InterruptedException {
if (interruptMode == THROW_IE)
throw new InterruptedException();
else if (interruptMode == REINTERRUPT)
selfInterrupt();
}
看到这里, 就将Condition看完了, Condition中还有一下带超时机制的 await, 不抛异常的await, signalAll这些方法, 但原理与await, signal相差不大, 就不一一分析了.
Java 线程中断
Thread类中线程中断相关的几个方法
// Thread 类中的实例方法,持有线程实例引用即可检测线程中断状态
public boolean isInterrupted() {}
// Thread 中的静态方法,检测调用这个方法的线程是否已经中断
// 如果当前线程已经处于中断状态,那么该方法返回 true,同时将中断状态重置为 false
public static boolean interrupted() {}
// Thread 类中的实例方法,用于设置一个线程的中断状态为 true
public void interrupt() {}
InterruptedException 异常
当阻塞方法收到中断请求的时候就会抛出InterruptedException异常