CyclicBarrier和CountDownLatch都是用于线程同步的工具类,但它们各自的使用场景和特性不同。以下是两者之间的主要区别:
CyclicBarrier
-
重用性:
CyclicBarrier的最大特点是它可以被重置后重用,即一组线程达到公共障碍点被释放后,CyclicBarrier可以被重置以便下一次使用。 -
固定数量的线程:
CyclicBarrier需要在开始时指定等待的线程数量,当指定数量的线程已经到达barrier时,它们会同时被释放。 -
回调功能:
CyclicBarrier允许你提供一个Runnable任务,当计数器被减到0时,该任务会被执行。这个任务在CyclicBarrier中称作barrier action,通常用于在所有线程继续执行之前更新共享状态。 -
方法:
CyclicBarrier提供了await()方法,线程调用它等待其他线程。await()方法有两个版本:一个是可以被中断的,另一个加入了超时机制。
CountDownLatch
-
一次性:
CountDownLatch是一次性的,一旦计数器减到零,就不能再被重置,如果想要重新使用,需要创建新的CountDownLatch对象。 -
灵活性:
CountDownLatch不需要事先知道线程的数量,因为它只是对事件进行计数,而不是对线程进行计数。任意数量的线程可以使用同一个CountDownLatch来计数。 -
不提供回调功能:
CountDownLatch没有直接的回调功能或者barrier action的概念,当计数器减到0时,所有等待的线程会继续执行,而不执行特定的任务。 -
方法:
CountDownLatch提供countDown()方法来递减计数器,并且有await()方法来等待计数器达到0。同样,await()方法也有可以中断和超时的版本。
使用场景比较
-
CyclicBarrier:适用于固定大小的线程组需要多次等待彼此达到公共点(如多轮计算,每轮都需要等待所有线程完成当前轮的计算)。
-
CountDownLatch:适用于一个或多个线程需要等待一组事件发生后才能继续执行,而且这些事件可能由不同数量的线程触发(如应用程序初始化时需要等待多个服务启动)。
代码示例
CyclicBarrier示例
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
// 创建一个CyclicBarrier实例,添加一个所有线程到达后执行的任务
private static CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("Barrier Action Executed!"));
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
Thread thread = new Thread(new Task());
thread.start();
}
}
static class Task implements Runnable {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " is waiting on barrier");
barrier.await();
System.out.println(Thread.currentThread().getName() + " has crossed the barrier");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
CountDownLatch示例
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
// 创建一个CountDownLatch实例
private static CountDownLatch latch = new CountDownLatch(3);
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
Thread thread = new Thread(new Task());
thread.start();
}
try {
// 主线程等待其他线程完成
latch.await();
System.out.println("All tasks finished. Main thread can proceed.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class Task implements Runnable {
@Override
public void run() {
try {
// 模拟任务执行
Thread.sleep((long) (Math.random() * 1000));
System.out.println(Thread.currentThread().getName() + " finished task.");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 减少计数器的值
latch.countDown();
}
}
}
}
在这两个例子中,CyclicBarrierExample展示了一个能够重置的同步点,CountDownLatchExample则展示了如何等待一组事件发生后主线程继续执行。二者都是并发编程中非常有用的同步工具。