Java 中 CyclicBarrier 和 CountDownLatch 的区别

简介: 【8月更文挑战第22天】

概述

CyclicBarrierCountDownLatch 是 Java 中用于线程同步的两个并发工具。虽然这两个类都用于协调线程,但它们有不同的目的和用法。

CyclicBarrier

CyclicBarrier 是一个可重复使用的同步点,允许一组线程等待直到所有线程都到达该点。一旦所有线程都到达该点,屏障就会解除,所有线程都可以继续执行。

CountDownLatch

CountDownLatch 是一个一次性同步点,允许一个或多个线程等待直到一个计数器达到零。一旦计数器达到零,所有等待的线程都可以继续执行。

主要区别

  • 可重复使用性: CyclicBarrier 是可重复使用的,这意味着它可以在线程到达屏障后重置并再次使用。CountDownLatch 是不可重复使用的,一旦计数器达到零,它就完成了。
  • 等待条件: CyclicBarrier 等待所有线程到达屏障。CountDownLatch 等待计数器达到零。
  • 线程唤醒: CyclicBarrier 一次唤醒所有等待的线程。CountDownLatch 逐个唤醒等待的线程。

示例

以下示例演示了 CyclicBarrierCountDownLatch 之间的区别:

CyclicBarrier

public class CyclicBarrierExample {
   

    public static void main(String[] args) {
   
        // 创建一个具有 3 个参与者的 CyclicBarrier
        CyclicBarrier barrier = new CyclicBarrier(3);

        // 创建 3 个线程
        Thread t1 = new Thread(() -> {
   
            try {
   
                // 线程 1 到达屏障
                barrier.await();
                System.out.println("线程 1 已到达屏障。");
            } catch (Exception e) {
   
                e.printStackTrace();
            }
        });

        Thread t2 = new Thread(() -> {
   
            try {
   
                // 线程 2 到达屏障
                barrier.await();
                System.out.println("线程 2 已到达屏障。");
            } catch (Exception e) {
   
                e.printStackTrace();
            }
        });

        Thread t3 = new Thread(() -> {
   
            try {
   
                // 线程 3 到达屏障
                barrier.await();
                System.out.println("线程 3 已到达屏障。");
            } catch (Exception e) {
   
                e.printStackTrace();
            }
        });

        // 启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}

输出:

线程 1 已到达屏障。
线程 2 已到达屏障。
线程 3 已到达屏障。

在这个示例中,三个线程都到达屏障后,屏障解除,所有线程继续执行。

CountDownLatch

public class CountDownLatchExample {
   

    public static void main(String[] args) {
   
        // 创建一个具有 3 个计数的 CountDownLatch
        CountDownLatch latch = new CountDownLatch(3);

        // 创建 3 个线程
        Thread t1 = new Thread(() -> {
   
            try {
   
                // 线程 1 完成任务
                latch.countDown();
                System.out.println("线程 1 已完成任务。");
            } catch (Exception e) {
   
                e.printStackTrace();
            }
        });

        Thread t2 = new Thread(() -> {
   
            try {
   
                // 线程 2 完成任务
                latch.countDown();
                System.out.println("线程 2 已完成任务。");
            } catch (Exception e) {
   
                e.printStackTrace();
            }
        });

        Thread t3 = new Thread(() -> {
   
            try {
   
                // 线程 3 完成任务
                latch.countDown();
                System.out.println("线程 3 已完成任务。");
            } catch (Exception e) {
   
                e.printStackTrace();
            }
        });

        // 启动线程
        t1.start();
        t2.start();
        t3.start();

        // 等待所有线程完成任务
        try {
   
            latch.await();
            System.out.println("所有线程已完成任务。");
        } catch (Exception e) {
   
            e.printStackTrace();
        }
    }
}

输出:

线程 1 已完成任务。
线程 2 已完成任务。
线程 3 已完成任务。
所有线程已完成任务。

在这个示例中,三个线程完成任务后,计数器达到零,所有等待的线程继续执行。

总结

CyclicBarrierCountDownLatch 都是有用的并发工具,用于协调线程。CyclicBarrier 用于同步一组线程,直到它们都到达一个点,而 CountDownLatch 用于同步一个或多个线程,直到一个计数器达到零。选择哪一个工具取决于任务的需要。

目录
相关文章
|
1天前
|
Java
Java之CountDownLatch原理浅析
本文介绍了Java并发工具类`CountDownLatch`的使用方法、原理及其与`Thread.join()`的区别。`CountDownLatch`通过构造函数接收一个整数参数作为计数器,调用`countDown`方法减少计数,`await`方法会阻塞当前线程,直到计数为零。文章还详细解析了其内部机制,包括初始化、`countDown`和`await`方法的工作原理,并给出了一个游戏加载场景的示例代码。
Java之CountDownLatch原理浅析
|
7天前
|
Java
Java代码解释++i和i++的五个主要区别
本文介绍了前缀递增(++i)和后缀递增(i++)的区别。两者在独立语句中无差异,但在赋值表达式中,i++ 返回原值,++i 返回新值;在复杂表达式中计算顺序不同;在循环中虽结果相同但使用方式有别。最后通过 `Counter` 类模拟了两者的内部实现原理。
Java代码解释++i和i++的五个主要区别
|
15天前
|
Java
通过Java代码解释成员变量(实例变量)和局部变量的区别
本文通过一个Java示例,详细解释了成员变量(实例变量)和局部变量的区别。成员变量属于类的一部分,每个对象有独立的副本;局部变量则在方法或代码块内部声明,作用范围仅限于此。示例代码展示了如何在类中声明和使用这两种变量。
|
27天前
|
Java
Java基础之 JDK8 HashMap 源码分析(中间写出与JDK7的区别)
这篇文章详细分析了Java中HashMap的源码,包括JDK8与JDK7的区别、构造函数、put和get方法的实现,以及位运算法的应用,并讨论了JDK8中的优化,如链表转红黑树的阈值和扩容机制。
20 1
|
1月前
|
存储 缓存 Java
【用Java学习数据结构系列】HashMap与TreeMap的区别,以及Map与Set的关系
【用Java学习数据结构系列】HashMap与TreeMap的区别,以及Map与Set的关系
31 1
|
18天前
|
Java
Java代码解释静态代理和动态代理的区别
### 静态代理与动态代理简介 **静态代理**:代理类在编译时已确定,目标对象和代理对象都实现同一接口。代理类包含对目标对象的引用,并在调用方法时添加额外操作。 **动态代理**:利用Java反射机制在运行时生成代理类,更加灵活。通过`Proxy`类和`InvocationHandler`接口实现,无需提前知道接口的具体实现细节。 示例代码展示了两种代理方式的实现,静态代理需要手动创建代理对象,而动态代理通过反射机制自动创建。
|
20天前
|
缓存 算法 Java
Java 中线程和纤程Fiber的区别是什么?
【10月更文挑战第14天】
51 0
|
安全 Java
java中CyclicBarrier的使用
java中CyclicBarrier的使用
|
10天前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?
|
1天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。