CountDownLatch理解一:与join的区别

2023-11-06

首先,我们来看一个应用场景1:

假设一条流水线上有三个工作者:worker0,worker1,worker2。有一个任务的完成需要他们三者协作完成,worker2可以开始这个任务的前提是worker0和worker1完成了他们的工作,而worker0和worker1是可以并行他们各自的工作的。

如果我们要编码模拟上面的场景的话,我们大概很容易就会想到可以用join来做。当在当前线程中调用某个线程 thread 的 join() 方法时,当前线程就会阻塞,直到thread 执行完成,当前线程才可以继续往下执行。补充下:join的工作原理是,不停检查thread是否存活,如果存活则让当前线程永远wait,直到thread线程终止,线程的this.notifyAll 就会被调用。

我们首先用join来模拟这个场景:

Worker类如下:

package com.concurrent.test3;

/**
 * 工作者类
 * @author ThinkPad
 *
 */
public class Worker extends Thread {

	//工作者名
	private String name;
	//工作时间
	private long time;
	
	public Worker(String name, long time) {
		this.name = name;
		this.time = time;
	}
	
	@Override
	public void run() {
		// TODO 自动生成的方法存根
		try {
			System.out.println(name+"开始工作");
			Thread.sleep(time);
			System.out.println(name+"工作完成,耗费时间="+time);
		} catch (InterruptedException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}	
	}
}

Test类如下:

package com.concurrent.test3;


public class Test {

	public static void main(String[] args) throws InterruptedException {
		// TODO 自动生成的方法存根

		Worker worker0 = new Worker("worker0", (long) (Math.random()*2000+3000));
		Worker worker1 = new Worker("worker1", (long) (Math.random()*2000+3000));
		Worker worker2 = new Worker("worker2", (long) (Math.random()*2000+3000));
		
		worker0.start();
		worker1.start();
		
		worker0.join();
		worker1.join();
		System.out.println("准备工作就绪");
		
		worker2.start();		
	}
}

运行test,观察控制台输出的顺序,我们发现这样可以满足需求,worker2确实是等worker0和worker1完成之后才开始工作的:

worker1开始工作
worker0开始工作
worker1工作完成,耗费时间=3947
worker0工作完成,耗费时间=4738
准备工作就绪
worker2开始工作
worker2工作完成,耗费时间=4513


除了用join外,用CountDownLatch 也可以完成这个需求。需要对worker做一点修改,我把它放在另一个包下:

Worker:

package com.concurrent.test4;

import java.util.concurrent.CountDownLatch;

/**
 * 工作者类
 * @author ThinkPad
 *
 */
public class Worker extends Thread {

	//工作者名
        private String name;
	//工作时间
	private long time;
	
	private CountDownLatch countDownLatch;
	
	public Worker(String name, long time, CountDownLatch countDownLatch) {
		this.name = name;
		this.time = time;
		this.countDownLatch = countDownLatch;
	}
	
	@Override
	public void run() {
		// TODO 自动生成的方法存根
		try {
			System.out.println(name+"开始工作");
			Thread.sleep(time);
			System.out.println(name+"工作完成,耗费时间="+time);
			countDownLatch.countDown();
			System.out.println("countDownLatch.getCount()="+countDownLatch.getCount());
		} catch (InterruptedException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}	
	}
}

Test:

package com.concurrent.test4;

import java.util.concurrent.CountDownLatch;


public class Test {

	public static void main(String[] args) throws InterruptedException {
		// TODO 自动生成的方法存根

		CountDownLatch countDownLatch = new CountDownLatch(2);
		Worker worker0 = new Worker("worker0", (long) (Math.random()*2000+3000), countDownLatch);
		Worker worker1 = new Worker("worker1", (long) (Math.random()*2000+3000), countDownLatch);
		Worker worker2 = new Worker("worker2", (long) (Math.random()*2000+3000), countDownLatch);
		
		worker0.start();
		worker1.start();
		
		countDownLatch.await();
		System.out.println("准备工作就绪");
		worker2.start();		
	}
}

我们创建了一个计数器为2的 CountDownLatch ,让Worker持有这个CountDownLatch 实例,当完成自己的工作后,调用countDownLatch. countDown() 方法将计数器减1。countDownLatch.await() 方法会一直阻塞直到计数器为0,主线程才会继续往下执行。观察运行结果,发现这样也是可以的:

worker1开始工作
worker0开始工作
worker0工作完成,耗费时间=3174
countDownLatch.getCount()=1
worker1工作完成,耗费时间=3870
countDownLatch.getCount()=0
准备工作就绪
worker2开始工作
worker2工作完成,耗费时间=3992
countDownLatch.getCount()=0


那么既然如此,CountDownLatch与join的区别在哪里呢?事实上在这里我们只要考虑另一种场景,就可以很清楚地看到它们的不同了。

应用场景2:

假设worker的工作可以分为两个阶段,work2 只需要等待work0和work1完成他们各自工作的第一个阶段之后就可以开始自己的工作了,而不是场景1中的必须等待work0和work1把他们的工作全部完成之后才能开始。

试想下,在这种情况下,join是没办法实现这个场景的,而CountDownLatch却可以,因为它持有一个计数器,只要计数器为0,那么主线程就可以结束阻塞往下执行。我们可以在worker0和worker1完成第一阶段工作之后就把计数器减1即可,这样worker0和worker1在完成第一阶段工作之后,worker2就可以开始工作了。

worker:

package com.concurrent.test5;

import java.util.concurrent.CountDownLatch;

/**
 * 工作者类
 * @author ThinkPad
 *
 */
public class Worker extends Thread {

	//工作者名
    private String name;
	//第一阶段工作时间
	private long time;
	
	private CountDownLatch countDownLatch;
	
	public Worker(String name, long time, CountDownLatch countDownLatch) {
		this.name = name;
		this.time = time;
		this.countDownLatch = countDownLatch;
	}
	
	@Override
	public void run() {
		// TODO 自动生成的方法存根
		try {
			System.out.println(name+"开始工作");
			Thread.sleep(time);
			System.out.println(name+"第一阶段工作完成");
			
			countDownLatch.countDown();
			
			Thread.sleep(2000); //这里就姑且假设第二阶段工作都是要2秒完成
			System.out.println(name+"第二阶段工作完成");
			System.out.println(name+"工作完成,耗费时间="+(time+2000));
			
		} catch (InterruptedException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}	
	}
}

Test:

package com.concurrent.test5;

import java.util.concurrent.CountDownLatch;


public class Test {

	public static void main(String[] args) throws InterruptedException {
		// TODO 自动生成的方法存根

		CountDownLatch countDownLatch = new CountDownLatch(2);
		Worker worker0 = new Worker("worker0", (long) (Math.random()*2000+3000), countDownLatch);
		Worker worker1 = new Worker("worker1", (long) (Math.random()*2000+3000), countDownLatch);
		Worker worker2 = new Worker("worker2", (long) (Math.random()*2000+3000), countDownLatch);
		
		worker0.start();
		worker1.start();	
		countDownLatch.await();
		
		System.out.println("准备工作就绪");
		worker2.start();
		
	}

}

观察控制台打印顺序,可以发现这种方法是可以模拟场景2的:

worker0开始工作
worker1开始工作
worker1第一阶段工作完成
worker0第一阶段工作完成
准备工作就绪
worker2开始工作
worker1第二阶段工作完成
worker1工作完成,耗费时间=5521
worker0第二阶段工作完成
worker0工作完成,耗费时间=6147
worker2第一阶段工作完成
worker2第二阶段工作完成
worker2工作完成,耗费时间=5384


最后,总结下CountDownLatch与join的区别:调用thread.join() 方法必须等thread 执行完毕,当前线程才能继续往下执行,而CountDownLatch通过计数器提供了更灵活的控制,只要检测到计数器为0当前线程就可以往下执行而不用管相应的thread是否执行完毕。


参考:http://stackoverflow.com/questions/21808814/whats-the-difference-between-cyclicbarrier-countdownlatch-and-join-in-java



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CountDownLatch理解一:与join的区别 的相关文章

  • Java Swing:从 JOptionPane 获取文本值

    我想创建一个用于 POS 系统的新窗口 用户输入的是客户拥有的金额 并且窗口必须显示兑换金额 我是新来的JOptionPane功能 我一直在使用JAVAFX并且它是不同的 这是我的代码 public static void main Str
  • 如何使用 Java 和 Selenium WebDriver 在 C 目录中创建文件夹并需要将屏幕截图保存在该目录中?

    目前正在与硒网络驱动程序和代码Java 我有一种情况 我需要在 C 目录中创建一个文件夹 并在该文件夹中创建我通过 selenium Web 驱动程序代码拍摄的屏幕截图 它需要存储在带有时间戳的文件夹中 如果我每天按计划运行脚本 所有屏幕截
  • 在 java 类和 android 活动之间传输时音频不清晰

    我有一个android活动 它连接到一个java类并以套接字的形式向它发送数据包 该类接收声音数据包并将它们扔到 PC 扬声器 该代码运行良好 但在 PC 扬声器中播放声音时会出现持续的抖动 中断 安卓活动 public class Sen
  • Android:捕获的图像未显示在图库中(媒体扫描仪意图不起作用)

    我遇到以下问题 我正在开发一个应用程序 用户可以在其中拍照 附加到帖子中 并将图片保存到外部存储中 我希望这张照片也显示在图片库中 并且我正在使用媒体扫描仪意图 但它似乎不起作用 我在编写代码时遵循官方的Android开发人员指南 所以我不
  • 无法展开 RemoteViews - 错误通知

    最近 我收到越来越多的用户收到 RemoteServiceException 错误的报告 我每次给出的堆栈跟踪如下 android app RemoteServiceException Bad notification posted fro
  • 多个 Maven 配置文件激活多个 Spring 配置文件

    我想在 Maven 中构建一个环境 在其中我想根据哪些 Maven 配置文件处于活动状态来累积激活多个 spring 配置文件 目前我的 pom xml 的相关部分如下所示
  • 列出jshell中所有活动的方法

    是否有任何命令可以打印当前 jshell 会话中所有新创建的方法 类似的东西 list但仅适用于方法 您正在寻找命令 methods all 它会打印所有方法 包括启动 JShell 时添加的方法 以及失败 被覆盖或删除的方法 对于您声明的
  • JavaMail 只获取新邮件

    我想知道是否有一种方法可以在javamail中只获取新消息 例如 在初始加载时 获取收件箱中的所有消息并存储它们 然后 每当应用程序再次加载时 仅获取新消息 而不是再次重新加载它们 javamail 可以做到这一点吗 它是如何工作的 一些背
  • 操作错误不会显示在 JSP 上

    我尝试在 Action 类中添加操作错误并将其打印在 JSP 页面上 当发生异常时 它将进入 catch 块并在控制台中打印 插入异常时出错 请联系管理员 在 catch 块中 我添加了它addActionError 我尝试在jsp页面中打
  • Mockito when().thenReturn 不必要地调用该方法

    我正在研究继承的代码 我编写了一个应该捕获 NullPointerException 的测试 因为它试图从 null 对象调用方法 Test expected NullPointerException class public void c
  • 为什么HashMap不能保证map的顺序随着时间的推移保持不变

    我在这里阅读有关 Hashmap 和 Hashtable 之间的区别 http javarevisited blogspot sg 2010 10 difference Between hashmap and html http javar
  • JRE 系统库 [WebSphere v6.1 JRE](未绑定)

    将项目导入 Eclipse 后 我的构建路径中出现以下错误 JRE System Library WebSphere v6 1 JRE unbound 谁知道怎么修它 右键单击项目 特性 gt Java 构建路径 gt 图书馆 gt JRE
  • 如何在 javadoc 中使用“<”和“>”而不进行格式化?

    如果我写
  • 如何从终端运行处理应用程序

    我目前正在使用加工 http processing org对于一个小项目 但是我不喜欢它附带的文本编辑器 我使用 vim 编写所有代码 我找到了 pde 文件的位置 并且我一直在从 vim 中编辑它们 然后重新打开它们并运行它们 重新加载脚
  • Android 中麦克风的后台访问

    是否可以通过 Android 手机上的后台应用程序 服务 持续监控麦克风 我想做的一些想法 不断聆听背景中的声音信号 收到 有趣的 音频信号后 执行一些网络操作 如果前台应用程序需要的话 后台应用程序必须能够智能地放弃对麦克风的访问 除非可
  • Java列表的线程安全

    我有一个列表 它将在线程安全上下文或非线程安全上下文中使用 究竟会是哪一个 无法提前确定 在这种特殊情况下 每当列表进入非线程安全上下文时 我都会使用它来包装它 Collections synchronizedList 但如果不进入非线程安
  • simpleframework,将空元素反序列化为空字符串而不是 null

    我使用简单框架 http simple sourceforge net http simple sourceforge net 在一个项目中满足我的序列化 反序列化需求 但在处理空 空字符串值时它不能按预期工作 好吧 至少不是我所期望的 如
  • 获取 JVM 上所有引导类的列表?

    有一种方法叫做findBootstrapClass对于一个类加载器 如果它是引导的 则返回一个类 有没有办法找到类已经加载了 您可以尝试首先通过例如获取引导类加载器呼叫 ClassLoader bootstrapLoader ClassLo
  • 如何修复 JNLP 应用程序中的“缺少代码库、权限和应用程序名称清单属性”?

    随着最近的 Java 更新 许多人都遇到了缺少 Java Web Start 应用程序的问题Codebase Permissions and Application name体现属性 尽管有资源可以帮助您完成此任务 但我找不到任何资源综合的
  • 将 List 转换为 JSON

    Hi guys 有人可以帮助我 如何将我的 HQL 查询结果转换为带有对象列表的 JSON 并通过休息服务获取它 这是我的服务方法 它返回查询结果列表 Override public List

随机推荐

  • Unity Animation -- 导入动画

    尽管Unity中的动画工具已经比较强大了 但对于非常复杂的或很长的动画 通常是由外部DCC创建 特别是对于角色动画来说 需要在动画过程中进行复杂的控制 因此我们通常需要将外部制作的动画导入到Unity中 动画能被共享吗 对于复杂的动画 制作
  • 阿里云-对象存储OSS

    大家好 我是 码赛客1024 今天我们一起来学习阿里云的对象存储服务OSS 一 介绍 阿里云对象存储OSS Object Storage Service 是一款海量 安全 低成本 高可靠的云存储服务 提供99 9999999999 12个9
  • 树的高度和深度

    用到树的数据结构时 经常会考虑树的高度和深度 但是lz总是搞混了 总虽然比较简单 就是个定义 记住就行了 但是因为长时间总是弄错 所以写一篇博文 加深一下印象 1 树的深度 树的深度可以这样理解 计算一个节点的深度 从根节点算起 记住从1开
  • LeetCode 之 Search for a Range

    题目链接 Search for a Range 对于一个按照升序排序的整数数组 找到给定目标值的开始位置和结束位置 且时间复杂度为O log n 如果在数组中找不到目标值 则返回 1 1 例如 数组为 5 7 7 8 8 10 和目标值8
  • PyTorch搭建LSTM实现时间序列预测(负荷预测)

    目录 I 前言 II 数据处理 III LSTM模型 IV 训练 V 测试 VI 源码及数据 I 前言 在上一篇文章深入理解PyTorch中LSTM的输入和输出 从input输入到Linear输出 中 我详细地解释了如何利用PyTorch来
  • qt中复制文件的功能

    1 利用qt来复制文件 首先新建个工程文件 拖入按钮 复制文件 2 需要引入的头文件如下 include
  • 六月学习记录

    六月学习记录 6 1 Android消息传递之Handler消息机制 four articles http www cnblogs com whoislcj p 5590615 html 6 2 代码审查工具phabricator 6 4
  • 7 Linux 内核移植

    一 编译 ST 的 Linux 系统 1 压缩源码 首先先下载 ST 官方源码 之前章节已经下载过了 直接输入以下命令 cd linux atk mpl stm32mp1 openstlinux 5 4 dunfell mp1 20 06
  • pycharm 2023.1.2 去掉版本控制

    pycharm 2023 1 2 去掉版本控制
  • 优秀程序员应该具备的12种能力

    1 编程语言能力 不用多说 作为合格的计算机程序员 精通一门语言是必须的 这种精通 不是说看了一本编程宝典 抄了几个程序就能说精通的 要靠长时间的积累 2 编码能力 我曾经看很多程序员写的代码 在同一个jsp或者java源文件里 出现了三种
  • WinForm控件Chart的图表类型

    将就看 后期再加目录导航2020 10 23 折线图 折线图通常使用一条线来连接一系列不同的数据点 这样的直线是直线 样条曲线或阶梯 折线图更简单 因此还可以使您可视化多个系列而不会像条形图那样重叠 以下是折线图的不同类型 折线图 折线图使
  • C\C++各种变量存放区域(代码、数据、堆、栈)

    C C 各种变量存放区域 代码 数据 堆 栈 文章目录 C C 各种变量存放区域 代码 数据 堆 栈 变量 数据 变量 数据存放区域 练习 请说明下面的指针分别指向什么位置 BSS Block Started by Symbol 区 为什么
  • numpy实现卷积两种方法

    暂时针对二维卷积 方法1 按定义计算 def numpy conv inputs myfilter h ori w ori inputs shape h k w k myfilter shape h new w new h ori h k
  • 如何控制小程序的tabBar的显示和隐藏

    1 使用wx hideTabBar 方法隐藏tabBar 2 使用wx showTabBar 方法显示tabBar 参考文档
  • 适配器模式代码示例

    package com example shipeiqi public interface ThreeElectricOutlet 用来刻画三相插座 public abstract void connectElectricCurrent p
  • maven-metadata.xml使用

    最近在开发过程中 发现工程中的一些包有问题 主要分为以下两种 1 最新包 版本号未变 只是被更新了 少了一个方法 2 jar包被更新了 但是无法更新最新的包下来 首先先确定一下更新包会导致更新哪些文件 下面都以a 1 0 1 snapsho
  • 学习笔记:机器学习之支持向量机(一、最大间隔算法)

    活动地址 CSDN21天学习挑战赛 1 简介 支持向量机也是一种二分类模型 它是通过在特征空间中建立间隔最大的分类器 这是有别于感知机模型的一点 支持向量机可分为线性可分支持向量机 线性支持向量机 非线性支持向量机 2 函数间隔 几何间隔
  • node后台快速开发框架

    任何可以用 JavaScript 来写的应用 最终都将用 JavaScript 来写 node js是用JS语言编写的 JaveScript已经是世界上最流行的语言之一 用它可以实现任何功能需求 无论是后台开发 前端 App 桌面应用程序
  • 华为OD机试 C++ 最佳植树距离

    题目 小明得到了一项任务 在一个沙地里种树 不过 有些地方不能种树 只能选特定的位置来种 为了让树之间的空间尽可能大 从而更好地防沙 你要帮小明决定每棵树应该种在哪里 举个例子 如果合适的种树地点是1 3 5 6 7 10 13 你有3棵树
  • CountDownLatch理解一:与join的区别

    首先 我们来看一个应用场景1 假设一条流水线上有三个工作者 worker0 worker1 worker2 有一个任务的完成需要他们三者协作完成 worker2可以开始这个任务的前提是worker0和worker1完成了他们的工作 而wor