MST

星途 面试题库

面试题:Java中CyclicBarrier与CountDownLatch的区别及适用场景

在Java多线程并发工具类中,CyclicBarrier和CountDownLatch都用于线程间的同步,请详细阐述它们的区别,包括功能、使用场景、是否可复用等方面,并举例说明在哪些实际业务场景中会优先选择使用其中一个而非另一个。
10.5万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

功能区别

  • CyclicBarrier:允许一组线程相互等待,直到到达某个公共屏障点。它内部维护一个计数器,每调用一次await()方法,计数器减1,当计数器减为0时,所有等待的线程被释放,且计数器会重置,可以被再次使用。
  • CountDownLatch:允许一个或多个线程等待,直到其他一组线程完成操作。它同样有一个计数器,但是该计数器只能递减一次,不会重置,当调用countDown()方法将计数器减为0时,等待的线程被释放。

使用场景区别

  • CyclicBarrier:适用于多个线程需要共同完成某个任务,并且在任务的某个阶段需要等待所有线程都到达后再继续执行的场景。例如,多个子任务并行计算,计算完成后需要汇总结果,在汇总前等待所有子任务都完成。
  • CountDownLatch:适用于一个线程需要等待其他多个线程完成某些操作后才能继续执行的场景。比如,主线程等待所有子线程初始化完成后再启动业务逻辑。

是否可复用区别

  • CyclicBarrier:可复用。因为计数器会在所有线程到达屏障点后重置,后续可以再次使用它进行线程同步。
  • CountDownLatch:不可复用。计数器一旦减为0,就无法再次使用,若要实现类似功能,需重新创建新的CountDownLatch实例。

实际业务场景举例

  • 优先选择CyclicBarrier的场景:在并行计算场景中,假设有多个线程分别计算不同部分的数据,最后需要将这些部分的计算结果汇总。每个线程计算完自己部分的数据后调用CyclicBarrierawait()方法等待其他线程,当所有线程都完成计算后,一起进入结果汇总阶段。代码示例如下:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {
    public static void main(String[] args) {
        int numThreads = 3;
        CyclicBarrier barrier = new CyclicBarrier(numThreads, () -> {
            System.out.println("All threads have reached the barrier, starting result aggregation.");
        });

        for (int i = 0; i < numThreads; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " is doing calculation.");
                    // 模拟计算
                    Thread.sleep((long) (Math.random() * 1000));
                    System.out.println(Thread.currentThread().getName() + " has finished calculation.");
                    barrier.await();
                    System.out.println(Thread.currentThread().getName() + " is participating in result aggregation.");
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
  • 优先选择CountDownLatch的场景:在应用程序启动时,可能有多个初始化任务需要并行执行,主线程需要等待所有初始化任务完成后才能启动核心业务逻辑。可以使用CountDownLatch来实现。代码示例如下:
import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) {
        int numTasks = 3;
        CountDownLatch latch = new CountDownLatch(numTasks);

        for (int i = 0; i < numTasks; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " is initializing.");
                    // 模拟初始化任务
                    Thread.sleep((long) (Math.random() * 1000));
                    System.out.println(Thread.currentThread().getName() + " has finished initializing.");
                    latch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }

        try {
            System.out.println("Main thread is waiting for all initializations to complete.");
            latch.await();
            System.out.println("All initializations are complete. Starting core business logic.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}