文章目录
- 前言
- 一、CyclicBarrier是什么?
- 二、CyclicBarrier工作原理
- 三、CyclicBarrier常用重要的方法
- 四、代码实战讲解
- 五、CyclicBarrier对比CountDownLatch
- 总结
前言
在多线程编程中,同步是一项关键的任务,尤其是当涉及到多个线程需要在某个共同点上同步执行时。Java 提供了丰富的并发工具来帮助开发人员实现线程之间的同步和协作。其中之一就是 CyclicBarrier(循环栅栏),这是一种非常有用的同步工具,能够让多个线程在某个共同点上同步等待,然后同时继续执行。
在本篇博客中,我们将深入探讨 CyclicBarrier 的工作原理、基本用法以及一些常见的应用场景。通过了解 CyclicBarrier,您将能够更好地利用它来优化多线程程序的设计和性能。让我们一起开始这次探索吧!
一、CyclicBarrier是什么?
CyclicBarrier,直译为“循环栅栏”,是 Java 并发编程中的一种同步工具。
循环:
CyclicBarrier 之所以被称为“循环”栅栏,是因为它具有循环使用的特性。一旦所有线程都到达栅栏点并且栅栏打开后,CyclicBarrier 将重新初始化,可以再次使用。这使得它非常适合那些需要多次同步的场景。
栅栏:
栅栏是一种用于同步线程的机制,类似于赛跑比赛中的起跑线。在 CyclicBarrier 中,所有参与线程必须在达到栅栏点之前等待,然后一起继续执行。这种同步机制确保了所有线程在某个共同点上同时进行操作,而不会出现竞争条件或数据不一致的情况。
总结:
综合起来,CyclicBarrier 是一种允许多个线程在某个共同点上同步等待,并且能够循环使用的同步工具。它提供了一种灵活而强大的机制,使得多线程程序可以更好地管理线程之间的协作和同步,从而提高程序的性能和可靠性。
二、CyclicBarrier工作原理
1.构造函数参数:
CyclicBarrier 的构造函数接受两个参数:参与线程数和栅栏动作。参与线程数表示在栅栏上等待的线程数量,而栅栏动作则是在所有线程到达栅栏时执行的操作。
2.栅栏点:
CyclicBarrier 中的栅栏点是指所有参与线程都必须等待的位置。当所有线程都到达栅栏点时,栅栏将打开,所有线程可以继续执行。
3.等待与释放:
每个线程在达到栅栏点之前都会调用 CyclicBarrier 的 await() 方法进行等待。当所有线程都调用了 await() 方法后,栅栏将打开,所有线程都可以继续执行。
4.栅栏动作:
在所有线程到达栅栏时,可以选择执行一个栅栏动作。这个动作是在构造函数中传递给 CyclicBarrier 的一个 Runnable 对象,可以用于执行一些额外的操作,比如说重置一些状态或者触发某个事件。
5.循环使用:
与 CountDownLatch 不同,CyclicBarrier 具有循环使用的特性。一旦所有线程都到达栅栏点并且栅栏打开后,CyclicBarrier 将重新初始化,可以再次使用。
三、CyclicBarrier常用重要的方法
四、代码实战讲解
下面这段代码会用到CyclicBarrier.await()这个核心方法,我先来为大家解释一下:
-
CyclicBarrier.await() 方法用于在线程中等待其他线程到达栅栏点。
-
当一个线程调用 await() 方法时,它会被阻塞,直到所有参与线程都到达栅栏点。
-
一旦所有线程都到达栅栏点,栅栏将打开,所有线程可以继续执行。
-
在所有线程都到达栅栏点之后,await() 方法将返回,并且线程将继续执行后续的代码。
-
如果某个线程在等待期间被中断,或者等待的线程之一遇到了异常,那么所有线程都将抛出 BrokenBarrierException 异常。
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class Race {
private static final int NUM_RUNNERS = 4;
private static final CyclicBarrier startLine = new CyclicBarrier(NUM_RUNNERS);
public static void main(String[] args) {
for (int i = 0; i < NUM_RUNNERS; i++) {
Thread runner = new Thread(new Runner("Runner " + (i + 1)));
runner.start();
}
}
static class Runner implements Runnable {
private final String name;
public Runner(String name) {
this.name = name;
}
@Override
public void run() {
try {
System.out.println(name + " 准备就绪,等待起跑信号。");
startLine.await(); // 等待其他选手准备就绪
System.out.println(name + " 起跑!");
// 运动员开始比赛...
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
-
在这个案例中,我们创建了一个 Race 类来模拟比赛。
-
比赛中有 4 名运动员,表示为 NUM_RUNNERS。
-
我们使用 CyclicBarrier 对象 startLine 来表示起跑线,所有运动员都将在这里等待。
-
每个运动员都是一个线程,表示为 Runner 类。在 run() 方法中,每个运动员首先等待其他选手准备就 绪,然后一起起跑。
-
主方法中创建了 4 个运动员线程,并启动它们。
运行效果:
五、CyclicBarrier对比CountDownLatch
上面讲了这么多,大家是不是觉得CyclicBarrier和CountDownLatch这两个工具好像啊,虽然 CyclicBarrier 和 CountDownLatch 都是 Java 中用于多线程编程的同步工具,但它们之间存在一些关键的区别:
1.循环使用 vs 单次使用:
CyclicBarrier 具有循环使用的特性,一旦所有线程都到达栅栏点并且栅栏打开后,它将重新初始化,可以再次使用。
CountDownLatch 则是一次性的计数器,在计数值减为 0 后就无法再次使用,除非重新创建新的 CountDownLatch 对象。
2.等待点的语义不同:
在 CyclicBarrier 中,所有参与线程必须等待在栅栏点上,直到所有线程都到达后,栅栏才会打开,所有线程一起继续执行后续操作。
而在 CountDownLatch 中,主线程或者某个特定线程需要等待其他线程执行完特定任务后才能继续执行。
3.计数方式不同:
CyclicBarrier 的计数器是线程数量,所有参与线程都要调用 await() 方法来等待,直到所有线程都到达栅栏点后,栅栏才会打开。
CountDownLatch 的计数器可以通过 countDown() 方法递减,一旦计数器减为 0,等待的线程就会被唤醒继续执行。
总结
以上就是我对 CyclicBarrier 的讲解,希望大家可以从中学习到如何利用 CyclicBarrier 实现多线程间的同步和协作。
CyclicBarrier 提供了一种简单而强大的机制,允许多个线程在达到共同点时相互等待,然后同时继续执行。通过理解其原理和用法,我们可以更好地编写并发程序,提高程序的性能和可靠性。
在实际应用中,合理地利用 CyclicBarrier 可以解决诸如并行计算、数据同步等问题,为我们的程序带来更大的灵活性和效率。希望本文能够帮助您更深入地理解并发编程中的 CyclicBarrier,并在您的编程实践中得到应用。
标签:栅栏,同步,编程,并发,线程,CyclicBarrier,等待,所有 From: https://blog.csdn.net/weixin_67589420/article/details/136718047