Java线程通信的几种方式

一、问题

有两个线程,A 线程向一个集合里面依次添加元素“abc”字符串,一共添加十次,当添加到第五次的时候,希望 B 线程能够收到 A 线程的通知,然后 B 线程执行相关的业务操作。线程间通信的模型有两种:共享内存和消息传递,以下方式都是基本这两种模型来实现的。

二、使用 volatile 关键字

基于 volatile 关键字来实现线程间相互通信是使用共享内存的思想。大致意思就是多个线程同时监听一个变量,当这个变量发生变化的时候 ,线程能够感知并执行相应的业务。这也是最简单的一种实现方式

public class TestSync {//定义共享变量来实现通信,它需要volatile修饰,否则线程不能及时感知static volatile boolean notice = false;public static void main(String[] args) {List<String>  list = new ArrayList<>();//线程AThread threadA = new Thread(() -> {for (int i = 1; i <= 10; i++) {list.add("abc");System.out.println("线程A添加元素,此时list的size为:" + list.size());try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}if (list.size() == 5)notice = true;}});//线程BThread threadB = new Thread(() -> {while (true) {if (notice) {System.out.println("线程B收到通知,开始执行自己的业务...");break;}}});//需要先启动线程BthreadB.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// 再启动线程AthreadA.start();}
}

三、使用 Object 类的 wait()/notify()

Object 类提供了线程间通信的方法:wait()、notify()、notifyAll(),它们是多线程通信的基础,而这种实现方式的思想自然是线程间通信。

注意: wait/notify 必须配合 synchronized 使用,wait 方法释放锁,notify 方法不释放锁。wait 是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行,只有其他线程调用了notify()notify并不释放锁,只是告诉调用过wait()的线程可以去参与获得锁的竞争了,但不是马上得到锁,因为锁还在别人手里,别人还没释放,调用 wait() 的一个或多个线程就会解除 wait 状态,重新参与竞争对象锁,程序如果可以再次得到锁,就可以继续向下运行。

public class TestSync {public static void main(String[] args) {//定义一个锁对象Object lock = new Object();List<String>  list = new ArrayList<>();// 线程AThread threadA = new Thread(() -> {synchronized (lock) {for (int i = 1; i <= 10; i++) {list.add("abc");System.out.println("线程A添加元素,此时list的size为:" + list.size());try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}if (list.size() == 5)lock.notify();//唤醒B线程}}});//线程BThread threadB = new Thread(() -> {while (true) {synchronized (lock) {if (list.size() != 5) {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("线程B收到通知,开始执行自己的业务...");}}});//需要先启动线程BthreadB.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//再启动线程AthreadA.start();}
}

由输出结果,在线程 A 发出 notify() 唤醒通知之后,依然是走完了自己线程的业务之后,线程 B 才开始执行,正好说明 notify() 不释放锁,而 wait() 释放锁。

四、使用JUC工具类 CountDownLatch

jdk1.5 之后在java.util.concurrent包下提供了很多并发编程相关的工具类,简化了并发编程代码的书写,CountDownLatch 基于 AQS 框架,相当于也是维护了一个线程间共享变量 state。

public class TestSync {public static void main(String[] args) {CountDownLatch countDownLatch = new CountDownLatch(1);List<String>  list = new ArrayList<>();//线程AThread threadA = new Thread(() -> {for (int i = 1; i <= 10; i++) {list.add("abc");System.out.println("线程A添加元素,此时list的size为:" + list.size());try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}if (list.size() == 5)countDownLatch.countDown();}});//线程BThread threadB = new Thread(() -> {while (true) {if (list.size() != 5) {try {countDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("线程B收到通知,开始执行自己的业务...");break;}});//需要先启动线程BthreadB.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//再启动线程AthreadA.start();}
}

五、使用 ReentrantLock 结合 Condition

public class TestSync {public static void main(String[] args) {ReentrantLock lock = new ReentrantLock();Condition condition = lock.newCondition();List<String> list = new ArrayList<>();//线程AThread threadA = new Thread(() -> {lock.lock();for (int i = 1; i <= 10; i++) {list.add("abc");System.out.println("线程A添加元素,此时list的size为:" + list.size());try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}if (list.size() == 5)condition.signal();}lock.unlock();});//线程BThread threadB = new Thread(() -> {lock.lock();if (list.size() != 5) {try {condition.await();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("线程B收到通知,开始执行自己的业务...");lock.unlock();});threadB.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}threadA.start();}
}

这种方式使用起来并不是很好,代码编写复杂,而且线程 B 在被 A 唤醒之后由于没有获取锁还是不能立即执行,也就是说,A 在唤醒操作之后,并不释放锁。这种方法跟 Object 的 wait()/notify() 一样。

六、基本 LockSupport 实现线程间的阻塞和唤醒

LockSupport 是一种非常灵活的实现线程间阻塞和唤醒的工具,使用它不用关注是等待线程先进行还是唤醒线程先运行,但是得知道线程的名字。

public class TestSync {public static void main(String[] args) {List<String> list = new ArrayList<>();//线程Bfinal Thread threadB = new Thread(() -> {if (list.size() != 5) {LockSupport.park();}System.out.println("线程B收到通知,开始执行自己的业务...");});//线程AThread threadA = new Thread(() -> {for (int i = 1; i <= 10; i++) {list.add("abc");System.out.println("线程A添加元素,此时list的size为:" + list.size());try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}if (list.size() == 5)LockSupport.unpark(threadB);}});threadA.start();threadB.start();}
}


---------------------
作者:一起努力啊啊啊啊
来源:CSDN
原文:https://blog.csdn.net/ChineseSoftware/article/details/118390388
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/283763.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PHP个人博客项目------切切歆语博客

2019独角兽企业重金招聘Python工程师标准>>> phpmysqlapache, ThinkPHP3.2框架开发 我的个人博客项目 适合新手练习 源码地址下载&#xff1a;https://github.com/DickyQie/php-myblog 转载于:https://my.oschina.net/zhangqie/blog/1785867

收发邮件之 MAILKIT

背景利用代码发送邮件在工作中还是比较常见的&#xff0c;相信大家都用过SmtpClient来处理发送邮件的操作&#xff0c;不过这个类以及被标记已过时&#xff0c;所以介绍一个微软推荐的库MailKit来处理。MailKit开源地址&#xff1a;https://github.com/jstedfast/MailKit需要邮…

IOS_SearchBar搜索栏及关键字高亮

搜索框的效果演示: 这个就是所谓的搜索框了,那么接下来我们看看如何使用代码来实现这个功能. 我所使用的数据是英雄联盟的英雄名单,是一个JSON数据的txt文件, JSON数据的处理代码如下所示: ?123456//获取文件的路径pathNSString *path [[NSBundle mainBundle] pathForResourc…

Java设计模式之(工厂模式)--简单工厂模式--工厂方法模式--抽象工厂模式

工厂模式&#xff1a; 工厂模式可以分为三类&#xff1a; 1&#xff09;简单工厂模式&#xff08;Simple Factory&#xff09; 2&#xff09;工厂方法模式&#xff08;Factory Method&#xff09; 3&#xff09;抽象工厂模式&#xff08;Abstract Factory&#xff09; 简单工…

今天很多 CTO 都是被干掉的,因为他没有成就业务

作者&#xff5c;乔新亮 编辑&#xff5c;邓艳琴 我可以丝毫不开玩笑地说&#xff0c;今天&#xff0c;很多传统企业里的研发都只是“工人”&#xff0c;哪怕是 CTO&#xff0c;充其量也只是“高级工人”&#xff0c;如果不转换思维去成就业务&#xff0c;就只能停留在工人级…

中航工业集团金网络(北京)电子商务有限公司副总经理刘正珩:航空“智”造的供应链支撑平台...

编者按 “十三五”时期是我国贸易发展的重要战略机遇期&#xff0c;物流产业发展迅速&#xff0c;智慧供应链已经成为推动流通大国向流通强国过程中的重要行动。6月2日&#xff0c;由上海市国有资产监督管理委员会、上海市邮政管理局、上海市商务委员会指导&#xff0c;上海市国…

创建、检查和反编译世界上(几乎)最短的 C# 程序

创建、检查和反编译世界上&#xff08;几乎&#xff09;最短的 C# 程序原文来自https://www.stevejgordon.co.uk/creating-inspecting-decompiling-the-worlds-smallest-csharp-program在这篇文章中&#xff0c;我认为创建世界上&#xff08;几乎&#xff09;最短的 C# 程序然后…

Linux下画原理图和PCB

Linux下画原理图和PCBWindows下大名鼎鼎的Allegro和经典的Protel 99SE都是不支持Linux操作系统的。做Linux驱动开发免不了要看一下原理图和PCB。一般的做法有三种&#xff1a; 1.主机使用Windows系统&#xff0c;将Linux装在VMWARE之类的虚拟机中这样能够使用Windows下的软件看…

配置中心 App Configuration (二):Feature Flag 功能开关特性

写在前面Web服务开发过程中我们经常有这样的需求&#xff1a;某些功能我必须我修改了配置才启用&#xff0c;比如新用户注册送券等&#xff1b;某个功能需到特定的时间才启用&#xff0c;过后就失效&#xff0c;比如春节活动等&#xff1b;某些功能&#xff0c;我想先对10%的用…

oracle临时表空间

--查看临时表空间SELECT * FROM v$tablespace;SELECT * FROM dba_tablespaces;--查看所有临时表空间文件SELECT * FROM dba_data_files;--查看临时临时表空间文件SELECT * FROM dba_temp_files;--查看临时表空间组SELECT * FROM dba_tablespace_groups; --查找默认临时表空间SE…

ES 2022 正式发布!有哪些新特性?

2022 年 6 月 22 日&#xff0c;第 123 届 Ecma 大会批准了 ECMAScript 2022 语言规范[1]&#xff0c;这意味着它现在正式成为标准。 1 ECMAScript 2022编辑 本次发布的编辑有&#xff1a; Shu-yu Guo[2] Michael Ficarra[3] Kevin Gibbons[4] 2 ECMAScript 2022有什么新内…

联想(Lenovo)小新310经典版进bios方法

1&#xff0c;找到novo按钮。 2&#xff0c;在关机的状态下桶一下小孔&#xff0c;不用任何操作&#xff0c;电脑进入bios选择界面。转载于:https://www.cnblogs.com/senior-engineer/p/6761457.html

C#中的匿名类型

这节来讲一下C#中的匿名类型。匿名类在C#中&#xff0c;我们可以不去显示的声明一个类&#xff0c;而是通过匿名类去临时声明一个类结构去帮助我们去完成一些功能。声明一个匿名类&#xff0c;我们可以像下面这样做&#xff1a;var Anonymousnew {name"charles",year…

MySQL之MHA高可用集群

目录 一、MHA概述 1.1.MHA 是什么 1.2.MHA 的组成 1.3.MHA 的特点 二、MHA搭建准备 2.1.实验思路 三、MHA搭建 3.1配置主从复制 3.2.安装 MHA 软件 3.3.故障模拟 3.4.故障修复 四、总结 一、MHA概述 1.1.MHA 是什么 1.MHA&#xff08;MasterHigh Availability&…

Tensorflow之安装

1.fellow the instruction of https://www.tensorflow.org/install/install_linux#installing_with_anaconda 2.anaconda安装&#xff0c;修改~/.bash_profile为 export PATH~/anaconda2/bin:/usr/local/cuda/bin:$PATHexport LD_LIBRARY_PATH/usr/local/cuda/lib64:$LD_LIBRAR…

2、Saltstack的数据系统

一、Grainsgrains是salt用来收集minion端底层系统信息的接口。比如&#xff0c;操作系统type、域名 、IP地址、内存及其他相关系统属性信息等。存储在minion端&#xff0c;用于保存minion端数据信息。minion启动时才加载grains信息&#xff0c;所以他时静态的&#xff0c;Grain…

配置中心 App Configuration (一):轻松集成到Asp.Net Core

写在前面在日常开发中&#xff0c;我这边比较熟悉的配置中心有&#xff0c;携程Apollo&#xff0c;阿里Nacos(配置中心&#xff0c;服务治理一体)之前文章&#xff1a;Asp.Net Core与携程阿波罗(Apollo)的第一次亲密接触总体来说&#xff0c;Apollo和Nacos社区都比较活跃&#…

stop-hbase.sh一直处于等待状态

今天关闭HBase时&#xff0c;输入stop-hbase.sh一直处于等待状态 解决方法&#xff1a; 先输入&#xff1a;hbase-daemon.sh stop master 再输入&#xff1a;stop-hbase.sh就可以关闭HBase集群了。 转载于:https://www.cnblogs.com/lijinze-tsinghua/p/8667761.html

shell编程100例

1、编写hello world脚本 #!/bin/bash# 编写hello world脚本echo "Hello World!"2、通过位置变量创建 Linux 系统账户及密码 #!/bin/bash# 通过位置变量创建 Linux 系统账户及密码#$1 是执行脚本的第一个参数,$2 是执行脚本的第二个参数 useradd "$1" …

sqlserver 分页

select top 10 numComImg.* from( select row_number() over(order by id asc) as rownumber,* from (select * FROM [TCCLine].[dbo].[CLine_CommonImage]) as comImg)as numComImg where rownumber>40select top 10 * --10 为页大小from [TCCLine].[dbo].[CLine_CommonIma…