一直搞不懂Java线程通信,这次终于明白了

本文章收录于《JAVA并发编程》合集中,本篇来介绍线程间通信,线程间通信 使线程成为一个整体,提高系统之间的 交互性,在提高CPU利用率的同时可以对线程任务进行有效的把控与监督 。
比如:多线程之间交替执行,多线程按顺序执行等,都需要使用线程通信技术,通过本篇文章您可以获得:
什么是线程通信,有什么作用
线程通信的三种实现方式
notifyAll的虚假唤醒问题,notify死锁问题
通过 ReentrantLock 实现精确唤醒
多线程按顺序执行的四种方案
线程通信常见面试题解析
相信你还有更多方式实现线程通信?不妨评论区告诉我们吧,高频率码字不易,觉得文章不错记得点赞支持一下哦!
线程间通信线程之间的交互我们称之为线程通信【Inter-Thread Communication,简称ITC】,指多个线程处理同一资源,但是任务不同
比如:小明放假在家,肚子饿了,如果发现没有吃的就会喊:妈,我饿了,弄点吃的,如果妈妈发现没有吃的了就会做菜,通知小明吃饭,总之:有菜通知小明吃饭,没菜小明通知妈妈做饭,简直吃货一个
此时就是两个线程对饭菜这同一资源有不同的任务,妈妈线程就是做饭,小明线程是吃饭,如果想要实现上边的场景,就需要妈妈线程和小明线程之间通信
要实现线程之间通信一般有三种方法:

  • 使用Object对象锁的wait()、notify()和notifyAll()方法
  • 使用Java5新增的JUC中的ReentrantLock结合Condition
  • 使用JUC中的CountDownLatch【计数器】
对象锁wait和notifyAll方法实现在此案例中,同一资源就是饭菜,小明对吃的操作是造,而妈妈对吃的操作是做
饭菜资源:
public class KitChenRoom {// 是否有吃的private boolean hasFood = false;// 设置同步锁,做饭和吃饭只能同时有一个在执行,不能边做边吃private Object lock = new Object();// 做饭public void cook() {// 加锁synchronized (lock) {// 如果有吃的,就不做饭if(hasFood) {// 还有吃的,先不做饭try {lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}// 否则就做饭,System.out.println(Thread.currentThread().getName() + "没吃的了,给娃做饭!");// 做好之后,修改为truehasFood = true;// 通知其他线程吃饭lock.notifyAll();}}// 吃饭public void eat() {synchronized (lock) {// 如果没吃的,就喊妈妈做饭,暂时吃不了if (!hasFood) {try {lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}// 否则就吃饭System.out.println(Thread.currentThread().getName() + "感谢老妈,恰饭,恰饭");// 吃完之后,修改为falsehasFood = false;// 通知其他线程吃饭lock.notifyAll();}}}测试类:
public class KitChenMain {public static void main(String[] args) {// 创建饭菜对象KitChenRoom chenRoom = new KitChenRoom();// 创建妈妈线程,做饭new Thread(() -> {for (int i = 0; i < 5; i++) {chenRoom.cook();}},"妈妈线程:").start();// 创建小明线程,吃饭new Thread(() -> {for (int i = 0; i < 5; i++) {chenRoom.eat();}},"小明线程:").start();}}运行结果:发现两个线程交替执行,没饭的时候妈妈做饭,有饭的时候小明就恰饭
一直搞不懂Java线程通信,这次终于明白了

文章插图
虚假唤醒在wait方法的源码注释中有这么一段话:
As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop翻译:在单参数版本中,中断和虚假唤醒是可能的,并且该方法应始终在循环中使用
一直搞不懂Java线程通信,这次终于明白了

文章插图
比如上边的 饭菜资源 代码中我们使用的是if判断是否有吃的
一直搞不懂Java线程通信,这次终于明白了

文章插图
如果此时我们再开启一个大明线程吃饭,开启一个爸爸线程做饭,此时会发生什么问题呢
改造测试类:再开启一个大明线程和一个爸爸线程
public class KitChenMain {public static void main(String[] args) {KitChenRoom chenRoom = new KitChenRoom();// 创建妈妈线程new Thread(() -> {for (int i = 0; i < 5; i++) {chenRoom.cook();}},"妈妈线程:").start();// 创建小明线程new Thread(() -> {for (int i = 0; i < 5; i++) {chenRoom.eat();}},"小明线程:").start();// 爸爸线程:做饭new Thread(() -> {for (int i = 0; i < 5; i++) {chenRoom.cook();}},"爸爸线程:").start();// 大明线程:吃饭new Thread(() -> {for (int i = 0; i < 5; i++) {chenRoom.eat();}},"大明线程:").start();}}


推荐阅读