Java 中用于线程同步类CountDownLatch和CyclicBarrier的区别

2024-05-03 02:20

本文主要是介绍Java 中用于线程同步类CountDownLatch和CyclicBarrier的区别,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

CountDownLatchCyclicBarrier 都是 Java 中用于线程同步的工具类,它们虽然在某些方面类似,但在用途和工作方式上有着明显的区别。下面我会详细解释这两者的区别:

1. 用途和设计目的

  • CountDownLatch:主要用于一个线程等待若干个其他线程完成某些操作后再继续执行。它的计数器只能使用一次,即一旦计数器倒数到零,就无法再被重置。
  • CyclicBarrier:主要用于让一组线程到达一个屏障(也称同步点)时被阻塞,直到最后一个线程到达屏障时,屏障开放,所有被阻塞的线程才会继续运行。与 CountDownLatch 不同的是,CyclicBarrier 是可重用的。

2. 工作机制

  • CountDownLatch:这个类使一个线程可以等待其他线程各自执行完毕后再执行。它通过一个计数器来实现,计数器的初始值为线程的数量。当一个线程完成了它的任务后,计数器的值就会减一。当计数器的值减到零时,它表示所有的线程都完成了任务,等待的线程被释放,继续执行。
  • CyclicBarrier:它也用一个计数器来实现,但 CyclicBarrier 的计数器由线程到达屏障时自动减少。当计数器的值减到零时,表示所有线程都达到了屏障,然后屏障将打开,所有等待的线程可以继续执行。此外,CyclicBarrier 可以在所有线程释放后执行一个可选的运行一次的动作(例如,一个回调),这是通过一个被称为 barrier action 的 Runnable 对象来实现的。

3. 应用场景

  • CountDownLatch:常用于主线程等待多个子线程的执行结果再继续主任务,或者等待某个服务被初始化后应用程序才开始运行。
  • CyclicBarrier:适用于多个线程相互等待至一个公共屏障点的场景,常用于多线程计算数据,数据分段处理后,需要等待所有线程处理完毕再继续处理的场景。

4. 可重用性

  • CountDownLatch 不能被重置,一旦计数器减到零,它就不能再被用于新的等待。
  • CyclicBarrier 可以被重置回初始状态,重复使用。

假设有一个需求,需要在系统启动时执行三个预加载任务。CountDownLatch 可以用于等待这三个预加载任务全部完成后,系统再继续其它的启动任务。而 CyclicBarrier 可能用于模拟一个场景,如迭代算法,其中多个线程需要等待彼此完成一轮计算,然后再同时开始下一轮计算。

举个例子:

CountDownLatch 示例

假设我们有一个应用程序,需要在处理用户请求之前加载一些初始配置。我们将使用CountDownLatch来确保所有的初始化任务都完成后,主线程再继续执行。

import java.util.concurrent.CountDownLatch;public class CountDownLatchExample {public static void main(String[] args) throws InterruptedException {int tasks = 3; // 假设有三个初始化任务CountDownLatch latch = new CountDownLatch(tasks);for (int i = 0; i < tasks; i++) {new Thread(new Worker(i, latch)).start();}latch.await();  // 主线程等待所有工作线程完成System.out.println("所有初始化任务完成。主线程继续执行。");}static class Worker implements Runnable {private final int taskNumber;private final CountDownLatch latch;Worker(int taskNumber, CountDownLatch latch) {this.taskNumber = taskNumber;this.latch = latch;}@Overridepublic void run() {try {Thread.sleep((long)(Math.random() * 1000));  // 模拟工作耗时System.out.println("任务 " + taskNumber + " 完成");} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {latch.countDown();  // 完成任务,计数器减1}}}
}

CyclicBarrier 示例

假设我们有一个并行计算任务,分为多个线程执行,每个线程执行完一部分计算后,需要等待其他线程完成相同的计算阶段,之后才能进入下一阶段。我们使用CyclicBarrier来同步这些线程。

 
import java.util.concurrent.CyclicBarrier;public class CyclicBarrierExample {private static class Task implements Runnable {private final int taskNumber;private final CyclicBarrier barrier;Task(int taskNumber, CyclicBarrier barrier) {this.taskNumber = taskNumber;this.barrier = barrier;}@Overridepublic void run() {try {System.out.println("任务 " + taskNumber + " 第一阶段执行");Thread.sleep((long)(Math.random() * 1000));barrier.await();  // 等待其他线程完成第一阶段System.out.println("任务 " + taskNumber + " 第二阶段执行");Thread.sleep((long)(Math.random() * 1000));barrier.await();  // 等待其他线程完成第二阶段System.out.println("任务 " + taskNumber + " 完成");} catch (Exception e) {e.printStackTrace();}}}public static void main(String[] args) {int numThreads = 3;  // 假设有三个任务CyclicBarrier barrier = new CyclicBarrier(numThreads, new Runnable() {@Overridepublic void run() {// 当所有线程都到达屏障时执行System.out.println("所有任务进入下一个阶段");}});for (int i = 0; i < numThreads; i++) {new Thread(new Task(i, barrier)).start();}}
}

在这两个示例中:

  • CountDownLatch用于主线程等待其他线程完成初始化任务后继续执行。
  • CyclicBarrier用于多个线程间相互等待,确保所有线程在进入下一计算阶段前都完成当前阶段的计算。

这篇关于Java 中用于线程同步类CountDownLatch和CyclicBarrier的区别的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/955631

相关文章

Java线程池核心参数原理及使用指南

《Java线程池核心参数原理及使用指南》本文详细介绍了Java线程池的基本概念、核心类、核心参数、工作原理、常见类型以及最佳实践,通过理解每个参数的含义和工作原理,可以更好地配置线程池,提高系统性能,... 目录一、线程池概述1.1 什么是线程池1.2 线程池的优势二、线程池核心类三、ThreadPoolE

Springboot请求和响应相关注解及使用场景分析

《Springboot请求和响应相关注解及使用场景分析》本文介绍了SpringBoot中用于处理HTTP请求和构建HTTP响应的常用注解,包括@RequestMapping、@RequestParam... 目录1. 请求处理注解@RequestMapping@GetMapping, @PostMappin

Java调用DeepSeek API的8个高频坑与解决方法

《Java调用DeepSeekAPI的8个高频坑与解决方法》现在大模型开发特别火,DeepSeek因为中文理解好、反应快、还便宜,不少Java开发者都用它,本文整理了最常踩的8个坑,希望对... 目录引言一、坑 1:Token 过期未处理,鉴权异常引发服务中断问题本质典型错误代码解决方案:实现 Token

Mybatis的mapper文件中#和$的区别示例解析

《Mybatis的mapper文件中#和$的区别示例解析》MyBatis的mapper文件中,#{}和${}是两种参数占位符,核心差异在于参数解析方式、SQL注入风险、适用场景,以下从底层原理、使用场... 目录MyBATis 中 mapper 文件里 #{} 与 ${} 的核心区别一、核心区别对比表二、底

SpringBoot整合AOP及使用案例实战

《SpringBoot整合AOP及使用案例实战》本文详细介绍了SpringAOP中的切入点表达式,重点讲解了execution表达式的语法和用法,通过案例实战,展示了AOP的基本使用、结合自定义注解以... 目录一、 引入依赖二、切入点表达式详解三、案例实战1. AOP基本使用2. AOP结合自定义注解3.

Java实现字符串大小写转换的常用方法

《Java实现字符串大小写转换的常用方法》在Java中,字符串大小写转换是文本处理的核心操作之一,Java提供了多种灵活的方式来实现大小写转换,适用于不同场景和需求,本文将全面解析大小写转换的各种方法... 目录前言核心转换方法1.String类的基础方法2. 考虑区域设置的转换3. 字符级别的转换高级转换

SpringBoot简单整合ElasticSearch实践

《SpringBoot简单整合ElasticSearch实践》Elasticsearch支持结构化和非结构化数据检索,通过索引创建和倒排索引文档,提高搜索效率,它基于Lucene封装,分为索引库、类型... 目录一:ElasticSearch支持对结构化和非结构化的数据进行检索二:ES的核心概念Index:

Java方法重载与重写之同名方法的双面魔法(最新整理)

《Java方法重载与重写之同名方法的双面魔法(最新整理)》文章介绍了Java中的方法重载Overloading和方法重写Overriding的区别联系,方法重载是指在同一个类中,允许存在多个方法名相同... 目录Java方法重载与重写:同名方法的双面魔法方法重载(Overloading):同门师兄弟的不同绝

Spring配置扩展之JavaConfig的使用小结

《Spring配置扩展之JavaConfig的使用小结》JavaConfig是Spring框架中基于纯Java代码的配置方式,用于替代传统的XML配置,通过注解(如@Bean)定义Spring容器的组... 目录JavaConfig 的概念什么是JavaConfig?为什么使用 JavaConfig?Jav

Java数组动态扩容的实现示例

《Java数组动态扩容的实现示例》本文主要介绍了Java数组动态扩容的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1 问题2 方法3 结语1 问题实现动态的给数组添加元素效果,实现对数组扩容,原始数组使用静态分配