线程间通信的几种实现方式

首先,要短信线程间通信的模型有两种:共享内存和消息传递,以下方式都是基本这两种模型来实现的。我们来基本一道面试常见的题目来分析:

题目:有两个线程A、B,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中添加一个元素,此时list中的元素个数为:" + 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()notifyaAl(),它们是多线程通信的基础,而这种实现方式的思想自然是线程间通信。

注意: wait和 notify必须配合synchronized使用,wait方法释放锁,notify方法不释放锁

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中添加一个元素,此时list中的元素个数为:" + 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中添加一个元素,此时list中的元素个数为:" + 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中添加一个元素,此时list中的元素个数为:" + 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在唤醒操作之后,并不释放锁。这种方法跟 Objectwait()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中添加一个元素,此时list中的元素个数为:" + list.size());try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}if (list.size() == 5)LockSupport.unpark(threadB);}});threadA.start();threadB.start();}
}

运行结果

线程间通信的几种实现方式相关推荐

  1. android 多线程间通信,android实现线程间通信的四种常见方式

    1,通过Handler机制 主线程中定义Handler,子线程发消息,通知Handler完成UI更新,Handler对象必须定义在主线程中,如果是多个类直接互相调用,就不是很方便,需要传递conten ...

  2. Android线程间通信的几种实现方式

    1. 通过Handler机制: private void one() {handler=new Handler(){@Overridepublic void handleMessage(Message ...

  3. android线程间通信的四种实现方式

    1,通过Handler机制. private void one() {handler=new Handler(){@Overridepublic void handleMessage(Message ...

  4. 线程间通信的三种方法 (转)

    http://www.cnblogs.com/puxidun/archive/2009/12/06/1618142.html 线程间通信的三种方法 多线程通信的方法主要有以下三种:  1.全局变量 进 ...

  5. python 线程通信的几种方式_进程间通信和线程间通信的几种方式

    进程 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础.在早期面向进程设计的计算机结构中,进程是程序的基本执行实体:在当代 ...

  6. 线程间通信的几种方式

    Java线程间通信: 1:线程上下文 2:共享内存 3:IPC通信 4:套接字(Socket),不同的机器之间进行通信 另外:附注通信内容: linux常用的进程间的通讯方式 (1).管道(pipe) ...

  7. JAVA线程间通信的几种方式

    今天在群里面看到一个很有意思的面试题: "编写两个线程,一个线程打印1~25,另一个线程打印字母A~Z,打印顺序为12A34B56C--5152Z,要求使用线程间的通信." 这是一 ...

  8. android线程间通信的几种方法_Android 技能图谱学习路线

    Java基础 Java Object类方法 HashMap原理,Hash冲突,并发集合,线程安全集合及实现原理 HashMap 和 HashTable 区别 HashCode 作用,如何重载hashC ...

  9. 线程间通信的几种方法_并发编程中的线程间通信

    线程通信的目标是使线程间能够互相发送信号.另一方面,线程通信使线程能够等待其他线程的信号. 线程通信常用的方式有: wait/notify 等待 Volatile 内存共享 CountDownLatc ...

最新文章

  1. node、Mongo项目如何前后端分离提供接口给前端
  2. Gut: 妊娠期糖尿病与孕妇及其新生儿微生物菌群失调有关(赵方庆作品)
  3. 在原神里钓鱼,有人竟然用上了深度强化学习,还把它开源了
  4. Katalon Studio自动化测试框架使用【1】--- 环境安装以及基础配置(MacOS)
  5. 无线网络嗅探中的Radiotap
  6. [CLR via C#]12. 泛型
  7. Python中的内存管理机制
  8. 追一科技热招职位来袭:AI新世界,不做旁观者
  9. 初学 Delphi 嵌入汇编[26] - 大小写字母转换
  10. linux系统安装arcsde,Linux操作系统安装ArcSDE10
  11. Python+tkinter不允许退出程序的思路和实现
  12. 2.Docker安装软件和制作自己的镜像
  13. 了解uni-app只需这一篇就足够了
  14. PW系列 | palette 体验
  15. 队爷的讲学计划 (强连通缩点+最短路)
  16. 解题笔记(15)——几个栈和递归的问题
  17. HDU - 6769
  18. docker容器正常启动宿主机却无法正常访问
  19. SAS学习笔记5:删除字符串空格-left/right/trim/strip/compress/compbl等函数的比较
  20. mapper报Cannot find class: com/kuang/dao/UserMapper

热门文章

  1. Bugzilla 中文镜像网站
  2. 2023年1月23日--2023年1月29日(osg+glsl+socket+ue,本周20小时,合计1899小时,剩余8101小时)
  3. linux arm寄存器,【寄存器】在armv8/armv7中ARM system control registers的区别
  4. 基于4G的物联网税控解决方案
  5. matlab在三维空间中三点绘制圆弧
  6. 整理网上资料---C标准库值篇二 :标准库源码下载地址、标准库手册下载地址
  7. 自建CA,并给服务器颁发证书,将该证书安装至浏览器
  8. 无穷级数(三)幂级数
  9. IMX6移植Linux4.1.15内核时需要添加矩阵按键的设备树信息
  10. 图论:具有至少两个顶点的简单无向图中一定存在度相同的结点