线程间通信的常用方式

1.简介

线程通信简单来说就是实现线程的交替工作,传递信息。例如在一个方法中我有两个线程A和B在运行,我希望线程A先向一个集合里面循环新增数据,当增加到第五次的时候,线程B才开始执行其他的操作。
线程间通信的模型有两种:共享内存消息传递

2.共享内存模型

1)volatile关键字

关于volatile关键字的作用详情可以看一下这本篇文章
volatile关键字的作用

使用volatile关键字简单来说就是多个线程同时监听一个变量,当该变量发生变化的时候,所有的监听的线程能够感知。

public class TestSyncVolatile {// 定义一个共享变量来实现通信,它需要是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() == 4) {notice = true;}}});// 实现线程BThread threadB = new Thread(() -> {while (true) {if (notice) {System.out.println("线程B收到通知,开始执行自己的业务...");System.out.println("此时list中的元素个数为:" + list.size());break;}}});// 需要先启动线程BthreadB.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// 再启动线程AthreadA.start();}}

3.消息传递

1)wait()/notify()(notifyAll())方法

wait()/notify()/notifyAll() 方法必须配合 synchronized关键字使用,关于Synchronized关键字的基本使用方法可以看一下这边文章:
Synchronized关键字的基本使用方法

wait()方法会暂时让出同步锁,以便其他正在等待此锁的线程可以得到锁并运行,其他线程调用了notify()/notifyAll()并不会立刻释放锁(notify()是随机释放单个调用当前锁的wait状态的线程,notifyAll()是释放全部调用当前锁的wait状态的线程),而是先等当前线程方法执行完才会释放锁,之后所有等待此锁的线程可以去参与获得锁的竞争,调用当前锁的 wait() 的一个或多个线程会解除 wait 状态,重新参与竞争对象锁, 得到锁的线程会继续执行。

public class TestSyncWait {public static void main(String[] args) {//定义一个锁对象Object lock = new Object();List<String> list = new ArrayList<>();// 线程AThread threadA = new Thread(() -> {for (int i = 1; i <= 10; i++) {synchronized (lock) {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();System.out.println("线程A还没有释放lock的同步锁");}}}});//线程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();}}


就比如上文的示例中,先启动线程B,线程B获取lock实例的锁,但是集合中没有5个参数,所以调用wait()方法,释放lock实例的锁,此时线程A获取并执行,当集合中参数个数到达5个时,调用lock.notify()释放lock实例的单个锁,但是此时本次循环还没结束,所以继续先执行完本次循环内容之后才释放释放lock实例的单个锁,之后线程A和线程B共同竞争lock锁,有可能线程A继续抢到锁继续循环,也有可能线程B抢到锁执行他的代码,所以每次启动都可能结果不同。

2)ReentrantLock

使用ReentrantLock和wait()/notify()的大体执行逻辑相同,但还是有不少区别:

wait()/notify():
锁调用notify()方法后,会等当前方法执行完才释放锁。
配合 synchronized关键字使用,synchronized为非公平锁,notify()之后所有等待锁的线程会去竞争获取锁。

ReentrantLock:
锁调用unlock方法后会立即释放锁。
公平锁,结合Condition使用,会释放指定的对象的锁。

public class TestSyncReentrantLock {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();System.out.println("线程A还没有释放lock的同步锁");}}});//线程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();}}

3)CountDownLatch

CountDownLatch使用方式与ReentrantLock类似,执行完countDown()方法后,不必等当前线程剩下方法执行完,可直接唤醒等待线程,执行其代码。

public class TestSyncCountDownLatch {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();System.out.println("线程A其他业务代码");}}});//线程BThread threadB = new Thread(() -> {try {countDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程B收到通知,开始执行自己的业务...");});//需要先启动线程BthreadB.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//再启动线程AthreadA.start();}}

4)LockSupport

LockSupport 是一种灵活的实现线程间阻塞和唤醒的工具。他可以根据线程名唤醒指定线程,唤醒之后不必等当前方法执行完,被唤醒线程可立刻执行其代码。

public class TestSyncLockSupport {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);System.out.println("线程A其他业务代码...");}}});threadA.start();threadB.start();}}

线程间通信的常用方式相关推荐

  1. android 线程间通信几种方式

    1.共享变量(内存) 2.管道 3.handle机制 runOnUiThread(Runnable) view.post(Runnable)

  2. C++ 九阴真经之线程间通信(消息队列)

    消息队列是线程间通信比较常用得方式,常用于解决经典模型生产者--消费者模型线程间得通信. 本文将结束基于C++标准库实现得消息队列,可以支持任意参数类型,任务参数数量. 为了方便后续线程池.异步队列得 ...

  3. 常用并发工具类(锁和线程间通信工具类)

    常用并发工具类总结 JUC 下的常用并发工具类(锁和线程间通信工具类),主要包括 ReentrantLock.ReentrantReadWriteLock.CountDownLatch.CyclicB ...

  4. linux系统线程通信的几种方式,Linux进程间通信-线程间通信

    Linux作为一种新兴的操作系统,几乎支持所有的Unix下常用的进程间通信方法:管道.消息队列.共享内存.信号量.套接口. 1.管道 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动 ...

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

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

  6. Java线程间通信-回调的实现方式

    2019独角兽企业重金招聘Python工程师标准>>> Java线程间通信-回调的实现方式 Java线程间通信是非常复杂的问题的.线程间通信问题本质上是如何将与线程相关的变量或者对象 ...

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

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

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

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

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

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

最新文章

  1. LTE: MIB和SIB,小区选择和重选规则
  2. R语言e1071包中的支持向量机:构建nu-classification类型的支持向量机SVM并分析不同nu值惩罚下模型分类螺旋线型(sprials)线性不可分数据集的表现
  3. Gabor滤波小结整理
  4. SAS 2.0:中端理想“零距离”?
  5. 树莓派-格式化硬盘(U盘)为ext4格式并挂载
  6. ArrayList和HashSet
  7. 浦发银行招聘计算机类笔试题,2019浦发银行招聘计算机模拟试题及答案
  8. quartz-misfire 错失、补偿执行
  9. BZOJ4517: [Sdoi2016]排列计数
  10. Linux内核入门-如何获取Linux内核源代码、生成配置内核
  11. classloader隔离练习
  12. 分区助手扩大c盘后自动修复_分区助手怎么扩大C盘?分区助手扩大C盘的方法
  13. 表达无序列表语义的html标签是,HTML语义标签的介绍和常用的语义标签
  14. Audio-driven Talking Face Video Generation with Learning-based Personalized Head Pose (译文)
  15. 房地产支撑不起大国崛起
  16. buildroot 执行 make savedefconfig 错误解决
  17. cpuz不显示服务器内存,cpu-z怎么看不到内存频率
  18. 实体 联系 模型mysql_实体关系模型和关系模型之间有什么区别?
  19. [Java] 枚举类型
  20. 河北省 2006年导游资格考试考生须知

热门文章

  1. flashcharge充电协议_iQOO 5充电评测:55W FlashCharge超快闪充加持
  2. 会使用mysql_mysql必知必会--使用MySQL
  3. 74161功能表_分析图 11 所示电路 74161 的 Q 3 为高电平时功能。 74194 功能表如表 1 所示。 74161 功能表如表 2 所示。 (7.2分)_学小易找答案...
  4. linux rm 删除文件,linux rm命令删除文件详解
  5. 物业系统管理源码 物业系统源码
  6. 乐视发布三款第4代超级电视 打造大屏游戏生态
  7. JAVA多小区物业管理系统源码,Java物业系统源码
  8. 项目38.2 打豆豆
  9. mysql服务器数据库
  10. QT从入门到入土(九)——TCP/IP网络通信(以及文件传输)