文章目录
CyclicBarrier(循环屏障)简介:
CyclicBarrier 是 Java 中 java.util.concurrent 包提供的一个同步辅助工具,用于在多个线程之间形成一个屏障点(barrier point),所有线程必须等待彼此达到这个屏障点,然后才能继续执行。
主要特点和用途:
1.同步多个线程: CyclicBarrier 允许一组线程相互等待,直到所有线程都到达一个公共的屏障点。
2.循环使用: 与一次性的 CountDownLatch 不同,CyclicBarrier 可以循环使用。一旦所有线程都到达屏障点,屏障就会打开,线程可以继续执行,然后 CyclicBarrier 又可以被重置以便下一轮使用。
3.线程协同: 适用于需要多个线程协同工作的场景,例如分治算法、模拟多轮比赛等。
基本用法:
CyclicBarrier 的主要构造方法如下:
CyclicBarrier(int parties)
其中,parties 参数是需要等待的线程数量。当调用 await 方法的线程数达到 parties 时,所有线程将被释放,屏障将被重置
1.CyclicBarrier应用流程图
1.1适用场景:
分阶段任务: 适用于多个线程分阶段执行任务,每个阶段的任务需要等待所有线程完成后才能继续。
数据计算: 适用于将数据拆分给多个线程进行计算,最后合并计算结果。
1.2 特点:
可循环使用: 支持循环使用,一旦所有线程到达屏障,可以进行下一轮的等待。
动态添加: 允许在屏障等待的过程中动态添加等待线程。
2.CountDownLatch应用流程图
2.1 适用场景:
并行任务: 适用于一组线程需要在主线程等待它们全部完成后才能继续执行的情况。
资源准备: 适用于主线程等待多个子线程完成资源准备工作后再开始工作。
2.2 特点:
不可重复使用: 一旦计数值变为零,就不能再次使用,需要重新创建。
静态计数: 初始化时就要指定计数值,不能在运行时动态改变计数值。
3.CyclicBarrier与CountDownLatch选择原则:
阶段性等待: 如果任务有多个阶段,每个阶段需要等待所有线程完成后再进行下一阶段,可以选择 CyclicBarrier。
一次性等待: 如果任务分为多个子任务,主线程需要等待所有子任务完成后才能继续执行,可以选择 CountDownLatch。
总的来说,CyclicBarrier 更适合处理多个线程相互等待的场景,而 CountDownLatch 更适合一组线程等待另一组线程完成的场景。
源码分析:
1.重要成员变量:
private final int parties; // parties 表示需要同步的线程数量,即到达屏障点的线程数目
private int count; // count 表示还未到达屏障点的线程数量,初始值为 parties
private final ReentrantLock lock = new ReentrantLock(); // 使用 ReentrantLock 作为底层同步器,用于保护内部状态的一致性
private final Condition trip = lock.newCondition(); // 使用 Condition 来实现线程的等待和唤醒机制
private final Runnable barrierCommand; // 可选的 Runnable,在所有线程到达屏障点时执行的任务
private Generation generation = new Generation(); // Generation 对象用于标识一轮循环,并在循环结束后重置计数器
2.构造方法
/**
* 构造一个新的 CyclicBarrier,指定参与同步的线程数量,并设置在所有线程到达屏障点时执行的可选任务。
*
* @param parties 同步的线程数量,即到达屏障点的线程数目
* @param barrierAction 在所有线程到达屏障点时执行的可选任务,如果不需要可传入 null
*/
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0)
throw new IllegalArgumentException("parties must be positive");
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
3.重要的方法
/**
* 等待所有线程到达屏障点,最多等待指定的超时时间。
*
* @param timeout 最大等待时间
* @param unit 时间单位
* @return 到达屏障点的线程在此时的序号
* @throws InterruptedException 如果当前线程在等待时被中断
* @throws BrokenBarrierException 如果在等待时屏障被损坏
* @throws TimeoutException 如果超过指定的超时时间
*/
public int await(long timeout, TimeUnit unit)
throws InterruptedException,
BrokenBarrierException,
TimeoutException {
return dowait(true, unit.toNanos(timeout));
}
/**
* 等待所有线程到达屏障点的核心方法,支持可中断和超时等待。
*
* @param timed 是否支持超时等待
* @param nanos 超时等待的纳秒数
* @return 到达屏障点的线程在此时的序号
* @throws InterruptedException 如果当前线程在等待时被中断
* @throws BrokenBarrierException 如果在等待时屏障被损坏
* @throws TimeoutException 如果超过指定的超时时间
*/
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException, TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock(); // 获取锁,确保在修改计数器等状态时的线程安全性
try {
final Generation g = generation;
// 检查线程是否已经被中断
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
// 计数器减一,表示当前线程已到达屏障点
int index = --count;
// 如果是最后一个线程到达屏障点,执行屏障动作并重置计数器
if (index == 0) {
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// 如果是第一个到达屏障的线程,创建一个新的 Generation 对象
if (index < 0) {
// 由于屏障已经被破坏,抛出 BrokenBarrierException
breakBarrier();
throw new BrokenBarrierException();
}
// 循环等待,直到计数器归零或者等待超时
for (;;) {
try {
if (!timed) {
trip.await();
} else if (nanos > 0L) {
nanos = trip.awaitNanos(nanos);
}
// 如果线程被中断,破坏屏障,抛出 InterruptedException
if (g != generation || Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
// 如果计数器已经归零,线程被唤醒,跳出循环
if (index == 0)
return index;
// 重新检查计数器,以防其他线程在唤醒时已经更新了计数器
index = count;
if (index < 0) {
// 由于屏障已经被破坏,抛出 BrokenBarrierException
breakBarrier();
throw new BrokenBarrierException();
}
// 如果超时等待时间已经用尽,抛出 TimeoutException
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
} catch (InterruptedException ie) {
// 如果线程被中断,破坏屏障,抛出 InterruptedException
if (g == generation && !broken) {
breakBarrier();
throw ie;
}
throw new InterruptedException();
}
}
} finally {
lock.unlock();
}
}
应用场景:
1.并行计算:
场景: 在并行计算中,可能有多个计算节点在进行某些计算,每个节点计算完成后需要等待其他节点完成,然后合并结果。
应用: 使用 CyclicBarrier 确保所有计算节点都完成计算后再执行后续合并操作。
import java.util.concurrent.CyclicBarrier;
public class ParallelComputingExample {
public static void main(String[] args) {
int numberOfNodes = 3;
CyclicBarrier barrier = new CyclicBarrier(numberOfNodes + 1);
for (int i = 0; i < numberOfNodes; i++) {
int nodeNumber = i + 1;
Thread nodeThread = new Thread(() -> {
// 节点计算操作
System.out.println("Node " + nodeNumber + " performing computation");
try {
barrier.await(); // 等待其他节点完成计算
} catch (Exception e) {
e.printStackTrace();
}
});
nodeThread.start();
}
// 主线程等待所有节点完成计算
try {
barrier.await();
System.out.println("All nodes have completed computation");
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.多阶段任务:
场景: 复杂任务可能分为多个阶段,每个阶段的执行都依赖于前一个阶段的结果。
应用: 在每个阶段末尾使用 CyclicBarrier,确保所有线程都完成当前阶段后,再进入下一个阶段。
import java.util.concurrent.CyclicBarrier;
public class MultiPhaseTaskExample {
public static void main(String[] args) {
int numberOfPhases = 3;
CyclicBarrier barrier = new CyclicBarrier(numberOfPhases + 1);
for (int i = 0; i < numberOfPhases; i++) {
int phaseNumber = i + 1;
Thread phaseThread = new Thread(() -> {
// 阶段任务操作
System.out.println("Phase " + phaseNumber + " executing");
try {
barrier.await(); // 等待其他线程完成当前阶段
} catch (Exception e) {
e.printStackTrace();
}
});
phaseThread.start();
}
// 主线程等待所有阶段完成
try {
barrier.await();
System.out.println("All phases have completed");
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.数据加载与初始化:
场景: 在系统启动时,需要加载和初始化多个模块的数据,每个模块的初始化可能是独立的。
应用: 使用 CyclicBarrier 来等待所有模块的数据加载和初始化完成,然后启动系统。
import java.util.concurrent.CyclicBarrier;
public class DataLoaderExample {
public static void main(String[] args) {
int numberOfModules = 3;
CyclicBarrier barrier = new CyclicBarrier(numberOfModules + 1);
for (int i = 0; i < numberOfModules; i++) {
int moduleNumber = i + 1;
Thread moduleThread = new Thread(() -> {
// 模块数据加载和初始化操作
System.out.println("Module " + moduleNumber + " loading and initializing data");
try {
barrier.await(); // 等待其他模块完成数据加载和初始化
} catch (Exception e) {
e.printStackTrace();
}
});
moduleThread.start();
}
// 主线程等待所有模块完成数据加载和初始化
try {
barrier.await();
System.out.println("All modules have completed data loading and initialization");
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.模拟多轮比赛:
场景: 在模拟比赛场景中,每一轮比赛可能包含多个阶段,每个阶段的执行需要等待所有参与者完成。
应用: 使用 CyclicBarrier 在每个比赛轮次的不同阶段等待所有参与者,模拟比赛的进行。
import java.util.concurrent.CyclicBarrier;
public class SimulationExample {
public static void main(String[] args) {
int numberOfRounds = 3;
CyclicBarrier barrier = new CyclicBarrier(numberOfRounds + 1);
for (int i = 0; i < numberOfRounds; i++) {
int roundNumber = i + 1;
Thread roundThread = new Thread(() -> {
// 模拟比赛的每一轮操作
System.out.println("Round " + roundNumber + " is in progress");
try {
barrier.await(); // 等待其他轮次完成
} catch (Exception e) {
e.printStackTrace();
}
});
roundThread.start();
}
// 主线程等待所有轮次完成
try {
barrier.await();
System.out.println("All rounds have completed");
} catch (Exception e) {
e.printStackTrace();
}
}
}
5.批量任务并发执行:
场景: 需要处理大量数据,可以将数据分成多个批次进行并发处理,但每个批次内部需要保持同步。
应用: 使用 CyclicBarrier 在每个批次内等待所有线程完成当前批次的处理,确保批次内的同步。
import java.util.concurrent.CyclicBarrier;
public class BatchProcessingExample {
public static void main(String[] args) {
int batchSize = 3;
CyclicBarrier barrier = new CyclicBarrier(batchSize + 1);
for (int i = 0; i < batchSize; i++) {
int batchNumber = i + 1;
Thread batchThread = new Thread(() -> {
// 批量任务操作
System.out.println("Batch " + batchNumber + " processing");
try {
barrier.await(); // 等待其他批次完成
} catch (Exception e) {
e.printStackTrace();
}
});
batchThread.start();
}
// 主线程等待所有批次完成
try {
barrier.await();
System.out.println("All batches have completed processing");
} catch (Exception e) {
e.printStackTrace();
}
}
}
6.多线程流水线:
场景: 在多线程流水线中,每个线程负责流水线的一个阶段。
应用: 使用 CyclicBarrier 在每个阶段等待所有线程,确保流水线的每个阶段都同步进行。
import java.util.concurrent.CyclicBarrier;
public class PipelineExample {
public static void main(String[] args) {
int numberOfStages = 3;
CyclicBarrier barrier = new CyclicBarrier(numberOfStages + 1);
for (int i = 0; i < numberOfStages; i++) {
int stageNumber = i + 1;
Thread stageThread = new Thread(() -> {
// 流水线阶段操作
System.out.println("Stage " + stageNumber + " processing");
try {
barrier.await(); // 等待其他阶段完成
} catch (Exception e) {
e.printStackTrace();
}
});
stageThread.start();
}
// 主线程等待所有阶段完成
try {
barrier.await();
System.out.println("All stages have completed processing");
} catch (Exception e) {
e.printStackTrace();
}
}
}