CyclicBarrier和CountDownLatch都是Java中常用的多线程同步工具,它们主要用来协调多个线程之间的行为,以便达到某种共同目标。虽然它们有一些相似之处,但在应用场景和使用方法上也存在着比较明显的区别。
1、CyclicBarrier
CyclicBarrier是一个可重用的屏障,它允许多个线程等待彼此,并在所有线程都准备就绪后再统一执行代码。每个线程的执行先调用await()方法,然后被阻塞直到所有线程都调用了该方法后才会往下执行。而且,CyclicBarrier还可以指定一个barrierAction,在所有线程到达屏障后自动执行。
以计算矩阵乘积为例,使用CyclicBarrier时可以将其中的计算过程分成若干个子任务,然后将子任务分配给不同线程来计算。当所有的线程都已经完成了自己的计算后,程序就可以进入下一个阶段,将子矩阵的结果合并起来得到整个矩阵的乘积。通过CyclicBarrier实现多线程计算矩阵乘积的示例代码如下:
public class CyclicBarrierDemo {
private static final int N = 4;
private static final int NR = N * N;
private static float[] a = new float[NR];
private static float[] b = new float[NR];
private static float[] c = new float[NR];
private static CyclicBarrier barrier;
public static void main(String[] args) {
for (int i = 0; i < NR; i++) {
a[i] = (float) Math.random();
b[i] = (float) Math.random();
}
barrier = new CyclicBarrier(N, () -> System.out.println("所有线程已完成计算,结果已合并!"));
for (int i = 0; i < N; i++) {
new ComputeThread(i).start();
}
System.out.println("主线程结束!");
}
private static class ComputeThread extends Thread {
int id;
ComputeThread(int id) {
this.id = id;
}
@Override
public void run() {
int from = id * N;
int to = from + N;
for (int k = 0; k < N; k++) {
for (int i = from; i < to; i++) {
float sum = 0;
for (int j = k * N; j < (k + 1) * N; j++) {
sum += a[i * N + j] * b[j * N + k];
}
c[i] = sum;
}
try {
System.out.println("线程" + id + "已完成第" + (k + 1) + "次计算!");
barrier.await(); // 等待其他线程
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
}
2、CountdownLatch
CountdownLatch是一种非常基本的同步工具,它只有一个计数器,该计数器初始化为一个正整数,并且线程通过调用countDown()方法来减少计数器的值,当计数器减少到0时,所有等待线程开始执行。与CyclicBarrier不同的是,CountdownLatch的计数器只能使用一次,一旦计数器变成了0,就不能复位,因此称为“倒计数”屏障。
以同时实现多个网络请求并发的场景为例,可以在每个网络请求完成后调用CountDownLatch的countDown()方法,直到计数器值降为0之前,其它所有线程都被阻塞,然后才能继续执行相应的操作。下面给出一个简单的示例代码:
public class CountDownLatchDemo {
private static final int COUNT = 5;
private static List<String> results = new ArrayList<>();
private static ExecutorService executorService = Executors.newFixedThreadPool(COUNT);
private static CountDownLatch countDownLatch = new CountDownLatch(COUNT);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < COUNT; i++) {
final int n = i + 1;
executorService.submit(() -> {
String result = doRequest(n);
results.add(result);
System.out.println("请求" + n + "成功!");
countDownLatch.countDown();
});
}
countDownLatch.await(); // 等待所有请求完成
System.out.println("全部请求已完成,结果为:" + results);
executorService.shutdown();
}
private static String doRequest(int n) {
try {
Thread.sleep((long) (Math.random() * 5000));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "result" + n;
}
}
可以看出,CountDownLatch在应用场景上比较单一,适合在某个任务被拆分为多个独立子任务时,等待所有子任务执行完成。