近期看到了多线程有关 CountDownLatch和CyclicBarrier的区别 的面试题,顺带也了解下多线程这块的这部分内容。
CountDownLatch和CyclicBarrier的用法
‘CountDownLatch’和‘CyclicBarrier’都是Java中的同步工具类,它们有一些关键区别:
作用不同:CountDownLatch主要用于等待一组线程完成操作,而CyclicBarrier用于等待一组线程到达指定的位置。
实现方式不同:CountDownLatch通过一个计数器来控制线程的等待,而CyclicBarrier通过一个循环队列来控制线程的等待。
等待方式不同:CountDownLatch的等待方式是线程在等待其他线程完成操作,而CyclicBarrier的等待方式是在等待其他线程到达指定的位置。
循环等待:CyclicBarrier允许一组线程在到达指定的位置后继续等待其他线程到达指定的位置,直到所有线程都到达指定的位置,然后一起继续执行。
因此,在选择使用CountDownLatch还是CyclicBarrier时,需要根据具体的需求来选择。通常情况下,CountDownLatch更加适用于等待一组线程完成操作的场景,而CyclicBarrier更加适用于等待一组线程到达指定位置的场景。
接下来分开讲下:
CountDownLatch用法
CountDownLatch是一个Java并发工具类,用于实现等待多个线程完成操作的机制。CountDownLatch通过一个计数器来控制等待的线程数量,当计数器的值为0时,等待的线程可以继续执行。
demo示例
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
// 创建三个线程,分别执行不同的任务
new Thread(() -> {
System.out.println("任务1开始执行");
// 任务1执行完毕后,计数器减1
latch.countDown();
System.out.println("任务1执行完毕");
}).start();
new Thread(() -> {
System.out.println("任务2开始执行");
// 任务2执行完毕后,计数器减1
latch.countDown();
System.out.println("任务2执行完毕");
}).start();
new Thread(() -> {
System.out.println("任务3开始执行");
// 任务3执行完毕后,计数器减1
latch.countDown();
System.out.println("任务3执行完毕");
}).start();
// 等待所有任务完成
latch.await();
System.out.println("所有任务执行完毕");
}
}
执行结果:
任务1开始执行
任务1执行完毕
任务2开始执行
任务2执行完毕
任务3开始执行
任务3执行完毕
所有任务执行完毕
在这个示例中,我们创建了一个CountDownLatch实例,并设置了计数器的初始值为3。然后,我们创建了三个线程,分别执行不同的任务。在每个任务完成时,我们调用latch.countDown()方法,将计数器的值减1。当计数器的值为0时,await()方法会阻塞当前线程,直到所有任务完成。当所有任务完成后,await()方法会返回,并打印"所有任务执行完毕"。(注意:当计数器为0仍继续执行countDown方法则并发执行,即超出初始值的线程可能在 “所有任务执行完毕” 前后执行 )
CyclicBarrier用法
CyclicBarrier是一个同步工具类,它允许一组线程相互等待,直到所有线程都到达指定的位置,然后一起继续执行。CyclicBarrier的常用作用是用于表示一组操作的完成,例如在多线程并发执行任务时,需要等待所有任务完成才能进行下一步操作。
demo示例
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
private static CyclicBarrier barrier = new CyclicBarrier(3);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 3; i++) {
new Thread(() -> {
long threadId = Thread.currentThread().getId();
System.out.println("线程" + threadId + "开始执行");
try {
barrier.await(); // 等待其他线程到达指定的位置
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("线程" + threadId + "执行完毕");
}).start();
}
}
}
执行结果:
线程20开始执行
线程22开始执行
线程21开始执行
线程21执行完毕
线程20执行完毕
线程22执行完毕
在这个示例中,我们创建了一个CyclicBarrier实例,并将其设置为3个线程参与同步。然后,我们创建了3个线程,每个线程都会先打印出自己的执行状态,然后调用barrier.await()方法等待其他线程到达指定的位置。最后,所有线程都会打印出执行完毕的状态。
需要注意的是,CyclicBarrier的同步位置是循环的,即当一组线程到达指定的位置后,它们会继续等待其他线程到达指定的位置,然后继续执行。因此,使用CyclicBarrier时需要特别注意循环次数的设置,避免死循环。(注意:若执行线程多余CyclicBarrier的容纳线程,则barrier.await()方法等待方法则不会执行,后续的执行完毕代码不会执行)
也可以参考这篇博客 CountDownLatch和CyclicBarrier区别和用法
有什么讲的不对的地方或见解也欢迎大家评论,谢谢。