MST

星途 面试题库

面试题:Java CyclicBarrier与CountDownLatch的对比及应用场景

详细对比Java中的CyclicBarrier和CountDownLatch,分析它们在原理、使用场景上的区别。并举例说明在何种实际场景下会优先选择使用CyclicBarrier而非CountDownLatch。
22.2万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

原理区别

  1. CyclicBarrier:它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。CyclicBarrier内部使用一个计数器,每当一个线程到达屏障点时,计数器减1,当计数器减到0时,所有等待的线程被释放,并且计数器会重置回初始值,以便下次使用,这也是它被称为“循环”的原因。
  2. CountDownLatch:通过一个计数器来控制线程的等待和释放。计数器初始化为一个给定的值,当一个线程调用countDown()方法时,计数器减1,当计数器减到0时,所有调用await()方法而处于等待状态的线程将被释放。但CountDownLatch的计数器无法重置,一旦计数器归零,就不能再次使用。

使用场景区别

  1. CyclicBarrier:适用于多个线程需要周期性地在某个点同步,然后继续执行后续任务的场景。例如,在一个计算任务中,多个线程分别处理不同部分的数据,每一轮处理完后,需要汇总结果,然后再开始下一轮处理。
  2. CountDownLatch:主要用于一个或多个线程等待其他一组线程完成某项操作之后才能继续执行的场景。比如,主线程等待所有子线程完成数据加载后,再进行数据的合并和分析。

优先选择CyclicBarrier的实际场景举例

假设我们有一个地图导航应用,需要实时更新地图路况信息。地图被划分为多个区域,每个区域有一个线程负责获取该区域的路况数据。每过一段时间(例如1分钟),所有区域的路况数据都要汇总并更新到地图上展示给用户。在这种场景下,就适合使用CyclicBarrier。因为每过1分钟,各个区域的线程都要在这个时间点上同步,将各自获取的数据汇总,然后又要开始下一轮的路况数据获取,这个过程是周期性的。而CountDownLatch无法满足这种周期性同步的需求,因为它的计数器不能重置,只能使用一次。

示例代码如下:

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("所有区域路况数据已汇总,更新地图");
        });

        for (int i = 0; i < numThreads; i++) {
            new Thread(new RoadConditionTask(barrier, "区域" + i)).start();
        }
    }
}

class RoadConditionTask implements Runnable {
    private CyclicBarrier barrier;
    private String region;

    public RoadConditionTask(CyclicBarrier barrier, String region) {
        this.barrier = barrier;
        this.region = region;
    }

    @Override
    public void run() {
        try {
            System.out.println(region + "开始获取路况数据");
            // 模拟获取路况数据的操作
            Thread.sleep((long) (Math.random() * 2000));
            System.out.println(region + "获取路况数据完成,等待其他区域");
            barrier.await();
            System.out.println(region + "开始下一轮路况数据获取");
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }
    }
}