CyclicBarrier是什么
- 字面意思回环栅栏。直接翻译是循环屏障。
- 通过它可以实现让一组线程等待至某个状态(屏障点)之后再全部同时执行。
- 叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。
- 通过ReentrantLock的"独占锁"和Conditon来实现一组线程的阻塞唤醒。
CyclicBarrier的使用场景
- 用于多线程计算数据,最后合并计算结果的场景。
- CyclicBarrier的计数器能够重置,屏障可以重复使用的特性,可以支持类似“人满发车”的场景。
CountDownLatch与CyclicBarrier的区别
- CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次
- CyclicBarrier还提供getNumberWaiting(可以获得CyclicBarrier阻塞的线程数量)、isBroken(用来知道阻塞的线程是否被中断)等方法。
- CountDownLatch会阻塞主线程,CyclicBarrier不会阻塞主线程,只会阻塞子线程。
- CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同。
- CountDownLatch一般用于一个或多个线程,等待其他线程执行完任务后,再执行。CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行。
- CyclicBarrier 还可以提供一个 barrierAction,合并多线程计算结果。
- CyclicBarrier是通过ReentrantLock的"独占锁"和Conditon来实现一组线程的阻塞唤醒的,而CountDownLatch则是通过AQS的“共享锁”实现
CyclicBarrier的常用方法
// 构造方法参数:parties表示屏障拦截的线程数量,每个线程调用 await 方法告诉 CyclicBarrier 我已经到达了屏障,然后当前线程被阻塞。
// 构造方法参数:barrierAction表示在线程到达屏障时,优先执行 barrierAction,方便处理更复杂的业务场景(该线程的执行时机是在到达屏障之后再执行)
public CyclicBarrier(int parties)
public CyclicBarrier(int parties, Runnable barrierAction)
// 屏障:指定数量的线程全部调用await()方法时,这些线程不再阻塞
// BrokenBarrierException 表示栅栏已经被破坏,破坏的原因可能是其中一个线程 await() 时被中断或者超时
public int await() throws InterruptedException, BrokenBarrierException
public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException
// 循环:通过reset()方法可以进行重置到原始状态
public void reset()
CyclicBarrier的合并计算场景
import java.util.Set;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CyclicBarrierTest1 {
// 保存每个学生的平均成绩
private ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();
// 定义线程池
private ExecutorService threadPool = Executors.newFixedThreadPool(5);
// 定义CyclicBarrier(回环栅栏)
private CyclicBarrier cb = new CyclicBarrier(5, () -> {
int result = 0;
Set<String> set = map.keySet();
for (String s : set) {
result += map.get(s);
}
System.out.println("这五人的平均成绩为:" + (result / 5) + "分");
});
public void count() {
for (int i = 0; i < 5; i++) {
threadPool.execute(new Runnable() {
@Override
public void run() {
// 获取学生平均成绩
int score = (int) (Math.random() * 40 + 60);
// 放到共享变量中
map.put(Thread.currentThread().getName(), score);
// 打印当前层级
System.out.println(Thread.currentThread().getName() + "同学的平均成绩为:" + score);
try {
// 执行完运行await(),等待所有学生平均成绩都计算完毕
cb.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
});
}
}
public static void main(String[] args) {
CyclicBarrierTest1 cb = new CyclicBarrierTest1();
cb.count();
}
}
- 运行结果:在所有人分数得到后,才会汇总结果
CyclicBarrier的“人满发车”场景
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class CyclicBarrierTest1 {
public static void main(String[] args) {
// 定义一个原子计数器
AtomicInteger counter = new AtomicInteger();
// 定义线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 1000, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100), (r) -> new Thread(r, counter.addAndGet(1) + " 号 "),
new ThreadPoolExecutor.AbortPolicy());
// 定义CyclicBarrier(回环栅栏)
CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> System.out.println("裁判:比赛开始~~"));
// 提交14个线程
for (int i = 0; i < 14; i++) {
threadPoolExecutor.submit(new Runner(cyclicBarrier));
}
}
/**
* 具体的任务
*/
static class Runner extends Thread {
private CyclicBarrier cyclicBarrier;
public Runner(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
try {
// 得到一个随机的睡眠时间进行睡眠
int sleepMills = ThreadLocalRandom.current().nextInt(1000);
Thread.sleep(sleepMills);
// 睡眠结束后,准备就绪
System.out.println(Thread.currentThread().getName() + " 选手已就位, 准备共用时: " + sleepMills + "ms"
+ cyclicBarrier.getNumberWaiting());
// 等待发令枪
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
- 运行结果:每满5人执行一次发车操作!最后一次不足5人,不发车!
CyclicBarrier源码分析的关注点
- 一组线程在触发屏障之前互相等待,最后一个线程到达屏障后唤醒逻辑是如何实现的。
- 删栏循环使用是如何实现的。
- 条件队列到同步队列的转换实现逻辑。
CyclicBarrier源码整体流程
- 第一阶段(await):获取锁(刚进入方法),释放锁,进入条件队列,阻塞线程
- 第二阶段(singal/singalAll):被其他调用singal/singalAll的线程唤醒,条件队列转同步队列,在释放锁的时候唤醒头结点的后续节点所在线程
- 第三阶段:获取锁(同步队列,有竞争还会阻塞),释放锁(唤醒头结点的后续节点所在线程)
CyclicBarrier的构造方法源码分析
/**
* 参数只有parties,他直接调用另一个构造方法
*/
public CyclicBarrier(int parties) {
this(parties, null);
}
/**
* 俩个参数的构造方法
* parties:表示屏障拦截的线程数量,每个线程调用 await 方法告诉 CyclicBarrier 我已经到达了屏障,然后当前线程被阻塞。
* barrierAction:表示在线程到达屏障时,优先执行 barrierAction,方便处理更复杂的业务场景(该线程的执行时机是在到达屏障之后再执行)
*/
public CyclicBarrier(int parties, Runnable barrierAction) {
// 传入的需要拦截的线程数为0,直接抛出异常
if (parties <= 0) throw new IllegalArgumentException();
// 指定线程数副本,用于重置
this.parties = parties;
// 指定计数器,真正的计数器
this.count = parties;
// 定义到达屏障后的触发机制
this.barrierCommand = barrierAction;
}
CyclicBarrier的等待方法await()源码分析
/**
* 无参数的等待方法:主要调用dowait方法
*/
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
/**
* 有参数的等待方法:主要调用dowait方法
*/
public int await(long timeout, TimeUnit unit)
throws InterruptedException,
BrokenBarrierException,
TimeoutException {
return dowait(true, unit.toNanos(timeout));
}
/**
* 等待的实际执行逻辑
*/
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
// 得到他本身的变量:ReentrantLock锁
final ReentrantLock lock = this.lock;
// 加锁:调用await,外层一定是ReentrantLock。调用wait,外层一定是synchronized。
lock.lock();
try {
// 定义一个代数的对象。经过一次栅栏,便是一代。
final Generation g = generation;
// 判断栅栏是否已经被破坏
if (g.broken)
throw new BrokenBarrierException();
// 判断当前线程是否被中断
if (Thread.interrupted()) {
// 冲破屏障的逻辑:设置当前代的broken状态为true,重置栅栏需要的线程数量,唤醒所有线程。
breakBarrier();
// 抛出中断异常
throw new InterruptedException();
}
// 将计数器减一,并且赋值给index变量
int index = --count;
// 计数器较少到0(最后一个到达屏障的线程),准备打开栅栏的逻辑。唤醒所有线程,并准备下一代。
if (index == 0) { // tripped
// 定义任务被执行的标记为false
boolean ranAction = false;
try {
// 拿到要在跳过栅栏之后的逻辑。(构造方法的第二个参数)
final Runnable command = barrierCommand;
// 方法存在,执行方法!
if (command != null)
command.run();
// 变更任务被执行的标记为true
ranAction = true;
// 更新栅栏的状态并唤醒所有线程
nextGeneration();
// 返回结果:0
return 0;
} finally {
// 如果栅栏的任务失败了
if (!ranAction)
// 冲破屏障的逻辑:设置当前代的broken状态为true,重置栅栏需要的线程数量,唤醒所有线程。
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
// 循环直到跳闸、中断、中断或超时
for (;;) {
try {
// 超时等待的逻辑判断:await无参的时候为false,有参数的时候为true
if (!timed)
// 调用条件队列的入队阻塞,直到被中断或者被唤醒。
// private final Condition trip = lock.newCondition(); // 从ReentrantLock获取的条件队列
trip.await();
else if (nanos > 0L)
// 带有超时条件的等待操作
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
// 如果还是当前代,并且没有被冲破屏障
if (g == generation && ! g.broken) {
// 冲破屏障的逻辑:设置当前代的broken状态为true,重置栅栏需要的线程数量,唤醒所有线程。
breakBarrier();
// 抛出当前的异常
throw ie;
} else {
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
// 中断当前线程
Thread.currentThread().interrupt();
}
}
// 判断栅栏是否已经被破坏
if (g.broken)
throw new BrokenBarrierException();
// 判断当前是否已经进入下一代
if (g != generation)
return index;
// 超时等待的逻辑判断:await无参的时候为false,有参数的时候为true,并且需要等待的超时时间小于0
if (timed && nanos <= 0L) {
// 冲破屏障的逻辑:设置当前代的broken状态为true,重置栅栏需要的线程数量,唤醒所有线程。
breakBarrier();
// 抛出超时异常
throw new TimeoutException();
}
}
} finally {
// 解锁操作
lock.unlock();
}
}
/**
* 冲破屏障的逻辑
*/
private void breakBarrier() {
// 将破坏标志变为true
generation.broken = true;
// 将屏障的线程数量还原
count = parties;
// 去唤醒其他呗拦截的线程
trip.signalAll();
}
/**
* 更新栅栏的状态并唤醒所有线程
*/
private void nextGeneration() {
// signal completion of last generation
// 不知道要唤醒哪一个,所以唤醒全部。上一代信号完成,唤醒其他的线程
trip.signalAll();
// set up next generation
// 还原下一代的数量。循环利用的关键点!
count = parties;
// 重新定义代数(下一代)
generation = new Generation();
}
使用到的条件队列的入队阻塞方法:await()源码分析
/**
* 条件队列的入队阻塞操作
*/
public final void await() throws InterruptedException {
// 线程被中断,抛出中断异常
if (Thread.interrupted())
throw new InterruptedException();
// 添加节点在条件等待队列中
Node node = addConditionWaiter();
// 释放锁逻辑
int savedState = fullyRelease(node);
// 定义中断模式状态为0
int interruptMode = 0;
// 判断是否在同步队列中
while (!isOnSyncQueue(node)) {
// 不在同步队列中,阻塞当前线程
LockSupport.park(this);
// 在等待的时候中断,返回不为0,说明被中断过,直接跳出循环
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// acquireQueued获取执行权的逻辑:获取到资源(false),被中断(true)
// 并且interruptMode在上方处理中是条件队列转化为同步队列
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
// 将值变为重新中断
interruptMode = REINTERRUPT;
// 下一个节点不为空的时候,清理掉链表中的状态为cancelled(1:已取消)状态的节点
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
// 状态被中断过,处理重新中断或者抛异常
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
/**
* 条件队列的入队阻塞操作,带有超时校验
*/
public final long awaitNanos(long nanosTimeout)
throws InterruptedException {
// 线程被中断,抛出中断异常
if (Thread.interrupted())
throw new InterruptedException();
// 添加节点在条件等待队列中
Node node = addConditionWaiter();
// 释放锁逻辑
int savedState = fullyRelease(node);
// 计算最后期限
final long deadline = System.nanoTime() + nanosTimeout;
// 定义中断模式状态为0
int interruptMode = 0;
// 判断是否在同步队列中
while (!isOnSyncQueue(node)) {
// 超时时间小于0
if (nanosTimeout <= 0L) {
// 转换(条件转同步)之后的取消等待逻辑
transferAfterCancelledWait(node);
// 跳出循环
break;
}
// 超时时间大于1000纳秒
if (nanosTimeout >= spinForTimeoutThreshold)
// 直接进行阻塞操作
LockSupport.parkNanos(this, nanosTimeout);
// 在等待的时候中断,返回不为0,说明被中断过,直接跳出循环
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
// 重新计算剩余的超时时间
nanosTimeout = deadline - System.nanoTime();
}
// acquireQueued获取执行权的逻辑:获取到资源(false),被中断(true)
// 并且interruptMode在上方处理中是条件队列转化为同步队列
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
// 下一个节点不为空的时候,清理掉链表中的状态为cancelled(1:已取消)状态的节点
if (node.nextWaiter != null)
unlinkCancelledWaiters();
// 状态被中断过,处理重新中断或者抛异常
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
// 返回相对于超时时间的剩余时间
return deadline - System.nanoTime();
}
/**
* 添加节点在条件等待队列(单向列表)中
*/
private Node addConditionWaiter() {
// 得到最后一个节点
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
// 去后一个节点不是空,并且状态不是-2(当前节点在等待condition,也就是在condition队列中)
if (t != null && t.waitStatus != Node.CONDITION) {
// 清理掉链表中的状态为cancelled(1:已取消)状态的节点
unlinkCancelledWaiters();
t = lastWaiter;
}
// 创建一个新的当前节点
Node node = new Node(Thread.currentThread(), Node.CONDITION);
// 单向列表的头结点为空,设置当前节点为头结点
if (t == null)
firstWaiter = node;
else
// 单向列表的头结点不为空,设置头结点的下一个节点是当前节点
t.nextWaiter = node;
// 设置单向列表的最后一个节点为当前节点
lastWaiter = node;
// 返回当前的节点
return node;
}
/**
* 清理掉链表中的状态为cancelled(1:已取消)状态的节点
*/
private void unlinkCancelledWaiters() {
// 得到第一个节点
Node t = firstWaiter;
// 定义标量trail
Node trail = null;
// 第一个节点不是空的
while (t != null) {
// 得到第二个节点
Node next = t.nextWaiter;
// 第二个节点不是-2(当前节点在等待condition,也就是在condition队列中)
if (t.waitStatus != Node.CONDITION) {
// 头结点的下一个节点变为null
t.nextWaiter = null;
// 如果trail为空,将断掉的节点复制到trail上。
if (trail == null)
firstWaiter = next;
else
// 如果trail不为空,将trail的下一个节点设置为当前节点
trail.nextWaiter = next;
// 第二个节点是空的,设置条件队列的尾结点为trail
if (next == null)
lastWaiter = trail;
}
else
// 节点是-2,标识节点在条件队列中,直接赋值
trail = t;
// 将下一个节点设置到t上,为了继续循环
t = next;
}
}
/**
* 释放锁逻辑
*/
final int fullyRelease(Node node) {
// 定时失败标志位true
boolean failed = true;
try {
// 获取state:在CyclicBarrier中表示的是重入次数
int savedState = getState();
// 释放重入锁
if (release(savedState)) {
// 释放锁成功
// 失败标记为false
failed = false;
// 返回重入次数
return savedState;
} else {
// 释放锁失败,抛异常
throw new IllegalMonitorStateException();
}
} finally {
// 当前线程失败了
if (failed)
// 设置这个节点的状态为取消状态
node.waitStatus = Node.CANCELLED;
}
}
/**
* 释放重入锁
*/
public final boolean release(int arg) {
// 尝试去释放:true为全部释放完成,false为没有全部释放完成
if (tryRelease(arg)) {
// 得到同步等待队列的头结点
Node h = head;
// 同步等待队列的头结点不是空,并且不在运行中,进行unpark操作。
if (h != null && h.waitStatus != 0)
// unpark去唤醒同步队列中的下一个线程
unparkSuccessor(h);
// 返回释放锁成功
return true;
}
// 返回释放锁失败
return false;
}
/**
* 尝试去释放锁:true为全部释放完成,false为没有全部释放完成
*/
protected final boolean tryRelease(int releases) {
// 当前的重入次数减去需要释放的重入次数
int c = getState() - releases;
// 判断当前线程不是持有锁的线程,抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
// 定义释放成功标记为false
boolean free = false;
// 如果锁都释放完了
if (c == 0) {
// 设置释放成功标记为true
free = true;
// 清空当前正在执行的线程
setExclusiveOwnerThread(null);
}
// 设置重入次数为最终释放的状态
setState(c);
// 返回是否全部释放锁的标记
return free;
}
/**
* unpark去唤醒下一个队列的线程
*/
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;
// SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;
// CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;
// PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;
// 对于ReentrantLock,只能出现-1的状态
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(CANCELLED,值为1,表示当前的线程被取消;)
if (s == null || s.waitStatus > 0) {
// 下一个节点直接置为null
s = null;
// 从尾部开始向前遍历,找到最前的一个处于正常阻塞状态的结点,直到节点重合
// 从尾部遍历的原因是为了防止在高并发场景下漏掉线程
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
// 过滤后的下一个节点不为null,唤醒他
if (s != null)
// 唤醒下一个节点
LockSupport.unpark(s.thread);
}
/**
* 判断是否在同步队列中
*/
final boolean isOnSyncQueue(Node node) {
// 当前节点的状态为-2(当前节点在等待condition,也就是在condition队列中),肯定不在同步队列中,返回false
// 当前节点的前驱节点是null:同步队列是双向链表,所以肯定不是同步队列
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
// 当前节点的next有值,说明是同步队列。
// 同步队列特有的下一个节点为next,上一个节点为prev。条件队列(单向列表)只有下一个节点nextWaiter。条件队列(单向列表)的next和prev没有值!。
if (node.next != null) // If has successor, it must be on queue
return true;
/*
* node.prev can be non-null, but not yet on queue because
* the CAS to place it on queue can fail. So we have to
* traverse from tail to make sure it actually made it. It
* will always be near the tail in calls to this method, and
* unless the CAS failed (which is unlikely), it will be
* there, so we hardly ever traverse much.
*/
// 从同步队列的队尾元素一直往前找节点
return findNodeFromTail(node);
}
/**
* 从同步队列的队尾元素一直往前找节点
*/
private boolean findNodeFromTail(Node node) {
// 得到同步队列的尾结点
Node t = tail;
// 从队尾元素一直往前找,找到一个相同的节点就表名他在同步队列中。t == null说明没有节点可以继续找,他不在同步队列中
for (;;) {
if (t == node)
return true;
if (t == null)
return false;
t = t.prev;
}
}
/**
* 检测在等待的时候中断
*/
private int checkInterruptWhileWaiting(Node node) {
// 没有被中断:直接返回0
// 被中断:判断后续的处理应该是THROW_IE(抛出InterruptedException)还是REINTERRUPT(重新中断)。
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
/**
* 转换(条件转同步)之后的取消等待逻辑:条件队列转同步队列成功,返回true。
*/
final boolean transferAfterCancelledWait(Node node) {
// CAS尝试将条件队列中的值变为正在执行状态
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
// 入同步队列
enq(node);
// 返回true
return true;
}
/*
* If we lost out to a signal(), then we can't proceed
* until it finishes its enq(). Cancelling during an
* incomplete transfer is both rare and transient, so just
* spin.
*/
// 当 node 被触发了 signal 方法时,node 就会被加到同步队列上
// 一直判断节点在不在同步队列上,直到调用出现在同步队列中,跳出循环
while (!isOnSyncQueue(node))
Thread.yield();
// 返回false
return false;
}
/**
* 设计精髓:100%创建队列或者100%入队
*/
private Node enq(final Node node) {
for (;;) {
// 得到之前的尾节点
Node t = tail;
// 之前的尾节点为空,需要进行初始化队列
if (t == null) { // Must initialize
// 通过CAS的方式将头节点设置为当前节点
if (compareAndSetHead(new Node()))
// 头结点设置成功后,复制给尾节点。只有一个节点的状态
tail = head;
} else {
// 设置当前线程节点的上一个节点是之前的尾节点
node.prev = t;
// cas尝试将尾节点设置为当前线程的节点
if (compareAndSetTail(t, node)) {
// 设置之前尾节点,现在的倒数第二个节点的下一个节点是当前线程节点
t.next = node;
// 返回当前节点!注意:这里是这个方法唯一返回的地方!也就是说初始化后还会继续循环一次来设置上一个下一个节点,然后进行返回。
return t;
}
}
}
}
/**
* 根据中断状态标志去处理后续逻辑
*/
private void reportInterruptAfterWait(int interruptMode)
throws InterruptedException {
// THROW_IE抛出中断异常
if (interruptMode == THROW_IE)
throw new InterruptedException();
// REINTERRUPT重新中断
else if (interruptMode == REINTERRUPT)
selfInterrupt();
}
/**
* 线程自我中断
*/
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
/**
* 阻塞逻辑,多了时间的校验
*/
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) {
// 得到当前线程
Thread t = Thread.currentThread();
// 设置被谁阻塞
setBlocker(t, blocker);
// 阻塞当前线程
UNSAFE.park(false, nanos);
// 唤醒后,清空blocker信息
setBlocker(t, null);
}
}
/**
* 最终的解锁操作
*/
public void unlock() {
sync.release(1);
}
/**
* 最终的解锁操作
*/
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
同步队列获取执行权:acquireQueued(final Node node, int arg) 源码分析
/**
* 获取执行权的逻辑:这里的精髓点是一定能保证线程要么获取到资源(false),要么被中断(true)
*/
final boolean acquireQueued(final Node node, int arg) {
// 设置失败的标志位为true
boolean failed = true;
try {
// 设置中断的标记位为false
boolean interrupted = false;
for (;;) {
// 得到当前节点的上一个(前置)节点,前置节点为null,会抛出空指针异常
final Node p = node.predecessor();
// 如果前置节点是头结点,并且尝试执行当前线程成功
if (p == head && tryAcquire(arg)) {
// 可以执行了,就把当前节点设置为头结点
setHead(node);
// 前置节点去掉引用,方便GC去回收
p.next = null; // help GC
// 设置失败的标志位为false
failed = false;
// 返回中断的标记位:false
return interrupted;
}
// 代码执行到这里,说明尝试获取锁,但是获取锁失败了。
// 阻塞前的准备工作操作成功(状态是-1的时候成功)
// 将线程阻塞,等待他去唤醒。唤醒后返回线程的中断状态!
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
// 设置中断的标记位:false
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
/**
* 获取锁失败后的准备逻辑,阻塞前的准备逻辑
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 获取前驱当前节点的等待状态
int ws = pred.waitStatus;
// 状态为-1的时候:当前节点的后继节点包含的线程需要运行,也就是unpark;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
// 前驱当前节点状态已经设置为SIGNAL,可以进行安全的阻塞
return true;
// 大于0(CANCELLED状态):表示当前的线程被取消;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
// 前驱节点已经因为超时或响应了中断,需要跳过这些状态大于0的节点,直到找到一个状态不是大于0的。
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.
*/
// 针对于ReentrantLock,到这里的状态只能为0或者PROPAGATE(-3)
// 通过CAS将前置节点的状态设置为SIGNAL(-1)
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
/**
* 状态为SIGNAL(-1)成功。他需要排队,所以直接调用park方法进行阻塞
*/
private final boolean parkAndCheckInterrupt() {
// 阻塞
LockSupport.park(this);
// unpark之后,返回当前的中断状态,并清除中断标志位
return Thread.interrupted();
}
/**
* 取消获取锁的逻辑
*/
private void cancelAcquire(Node node) {
// Ignore if node doesn't exist
// 忽略节点不存在的时候
if (node == null)
return;
// 设置当前节点的线程为null
node.thread = null;
// Skip cancelled predecessors:有前驱节点被取消,跳过所有被取消的
// 得到前驱节点
Node pred = node.prev;
// 前驱节点的状态大于0,被取消
while (pred.waitStatus > 0)
// 将前驱结点的前驱结点设置为当前节点的前驱结点。简单理解就是将当前节点的前驱节点设置为第一个找到的正常状态(<=0)的前驱节点
node.prev = pred = pred.prev;
// predNext is the apparent node to unsplice. CASes below will
// fail if not, in which case, we lost race vs another cancel
// or signal, so no further action is necessary.
// 获取当前节点的下一个节点
Node predNext = pred.next;
// Can use unconditional write instead of CAS here.
// After this atomic step, other Nodes can skip past us.
// Before, we are free of interference from other threads.
// 将当前节点状态设置为1(取消状态)。这里不用CAS的原因是这个执行完其他线程会跳过取消状态,这个执行前无其他线程在执行!
node.waitStatus = Node.CANCELLED;
// If we are the tail, remove ourselves.
// 如果当前节点是尾节点,将尾节点设置为上一个节点。简单理解就是移除当前节点。
if (node == tail && compareAndSetTail(node, pred)) {
compareAndSetNext(pred, predNext, null);
} else {
// 进入else说明node不是队尾(或者是队尾但是cas队尾失败(其实结果也不是队尾,因为被别的线程抢先了))
// If successor needs signal, try to set pred's next-link
// so it will get one. Otherwise wake it up to propagate.
// 定义一个状态标识
int ws;
// 筛选后的前驱节点不是头结点
// 并且当前节点状态为-1(等待唤醒)或者(当前节点不在运行或者不被取消(<= 0)并且可以将当前节点CAS到-1状态(等待唤醒))
// 并且前驱节点有线程持有!
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)
// CAS将前一个节点与下一个节点连接。简单理解就是跳过(取消)当前节点!
compareAndSetNext(pred, predNext, next);
} else {
// 唤醒下一个不被取消的节点!
unparkSuccessor(node);
}
// 当前节点的下一个节点取消指向
node.next = node; // help GC
}
}
唤醒逻辑signalAll()
/**
* 唤醒全部
*/
public final void signalAll() {
// AQS在标记运行的线程不是当前线程,抛异常
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
// 获取条件队列的头结点
Node first = firstWaiter;
// 条件队列的头结点不是null,尝试去唤醒全部
if (first != null)
doSignalAll(first);
}
/**
* 判断是不是唯一的线程,AQS在标记运行的线程是不是当前线程
*/
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
/**
* 尝试唤醒全部线程
*/
private void doSignalAll(Node first) {
// 条件队列的尾结点等于头结点等于null
lastWaiter = firstWaiter = null;
// 条件队列(单向列表)出队并进入同步队列(双向列表)
do {
// 得到当前节点的下一个节点
Node next = first.nextWaiter;
// 设置当前节点的下一个节点为null
first.nextWaiter = null;
// 条件队列转同步队列
transferForSignal(first);
// 当前节点等于下一个节点
first = next;
// 一直循环到下一个节点为null
} while (first != null);
}
/**
* 条件队列转同步队列
*/
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
// 如果无法更改等待状态,则节点已被取消。
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
// 当前节点进入条件队列,返回当前节点的上一个节点
Node p = enq(node);
// 获取上一个节点的状态
int ws = p.waitStatus;
// 节点状态大于0(1:已取消)或者当前节点的状态CAS到-1失败!不能在有需要的时候唤醒,那么现在就唤醒他!为了防止转移到同步队列尾部无法唤醒
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
// 真正的唤醒!
LockSupport.unpark(node.thread);
// 返回条件队列转同步队列成功
return true;
}
解锁:unlock()方法跟踪
/**
* unlock他直接调用了sync.release(1)
*/
public void unlock() {
sync.release(1);
}
/**
* 释放锁
*/
public final boolean release(int arg) {
// 尝试释放锁
if (tryRelease(arg)) {
// 释放锁成功,获取头结点
Node h = head;
// 头结点不为null并且当前节点的状态不在初始化状态
if (h != null && h.waitStatus != 0)
// unpark去唤醒队列中的下一个线程
unparkSuccessor(h);
// 返回解锁成功
return true;
}
// 返回解锁失败
return false;
}
/**
* 尝试解锁
*/
protected final boolean tryRelease(int releases) {
// 获取当前状态的state值与传入的releases。对应减去前面的重入次数
int c = getState() - releases;
// 当前线程不是现在获取锁的线程,抛异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
// 定义可以释放锁的标记位
boolean free = false;
// 回到了初始状态
if (c == 0) {
// 释放锁的标记位变为0
free = true;
// 设置持有锁的线程为null
setExclusiveOwnerThread(null);
}
// 设置AQS的状态
setState(c);
// 返回锁的标记位
return free;
}
/**
* unpark去唤醒下一个队列的线程
*/
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;
// SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;
// CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;
// PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;
// 对于ReentrantLock,只能出现-1的状态
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(CANCELLED,值为1,表示当前的线程被取消;)
if (s == null || s.waitStatus > 0) {
// 下一个节点直接置为null
s = null;
// 从尾部开始向前遍历,找到最前的一个处于正常阻塞状态的结点,直到节点重合
// 从尾部遍历的原因是为了防止在高并发场景下漏掉线程
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
// 过滤后的下一个节点不为null,唤醒他
if (s != null)
// 唤醒下一个节点
LockSupport.unpark(s.thread);
}
结束语
- 获取更多有价值的文章,让我们一起成为架构师!
- 关注公众号,可以让你对MySQL有非常深入的了解
- 关注公众号,每天持续高效的了解并发编程!
- 关注公众号,后续持续高效的了解spring源码!
- 这个公众号,无广告!!!每日更新!!!