文章目录

  • 3 线程间通信
    • 3.1 synchronized 实现案例
    • 3.2 虚假唤醒问题
    • 3.3 Lock 实现案例

3 线程间通信

线程间通信有两种实现方法:

关键字 synchronizedwait()/notify() 这两个方法一起使用可以实现等待/通知模式

Lock 接口中的 newContition() 方法返回 Condition 对象,Condition 类也可以实现等待/通知模式

用 notify()通知时,JVM 会随机唤醒某个等待的线程

使用 Condition 类可以进行选择性通知, Condition 比较常用的两个方法:

  1.  await() 会使当前线程等待,同时会释放锁,当其他线程调用 signal()时,线程会重新获得锁并继续执行2.  signal() 用于唤醒一个等待的线程

操作线程的时候,等待线程使用wait()
通知另外的线程操作用notify()、notifyAll()
假设有两个线程,该线程在执行过程中,判断值(不是该值等待,让其他线程抢),操作值,通知另外一个线程的调度

3.1 synchronized 实现案例

实现两个线程对 num 这个值操作,一个线程加1,一个线程减1,交替实现多次

// 创建一个资源类
class Share{// 设置临界资源private int number = 0;// 实现+1操作public synchronized void incr() throws InterruptedException {// 操作:判断、干活、通知if (number != 0) {// number不为0,等待// wait 有一个特点,在哪里睡,就在哪里醒this.wait();    }number++;System.out.print(Thread.currentThread().getName()+"::"+number);// 唤醒其他线程// 注意这里的通知是随机的,就是只能通知全部this.notifyAll();}// 实现-1操作public synchronized void decr() throws InterruptedException {// 操作:判断、干活、通知if (number != 1) {// number不为0,等待this.wait();}number--;System.out.println(Thread.currentThread().getName()+"::"+number);this.notifyAll();}
}
public class InterThreadCommunication {public static void main(String[] args) {Share share = new Share();new Thread(new Runnable() {@Overridepublic void run() {try {for (int i = 0; i < 100; i++) {share.incr();}} catch (InterruptedException e) {e.printStackTrace();}}},"A").start();new Thread(new Runnable() {@Overridepublic void run() {try {for (int i = 0; i < 100; i++) {share.decr();}} catch (InterruptedException e) {e.printStackTrace();}}},"B").start();}
}

输出结果如下

3.2 虚假唤醒问题

虚假唤醒主要出现在多线程中出现。

同样使用上述案例,现在有四个线程,分别为A,B,C,D,其中A,C线程做+1操作,B,D线程做-1操作,想要的结尾应该是A,C线程输出值为1,B,D线程输出值为0 。修改上述代码如下:

// 创建一个资源类
class Share{// 设置临界资源private int number = 0;// 实现+1操作public synchronized void incr() throws InterruptedException {// 操作:判断、干活、通知if (number != 0) {// number不为0,等待this.wait();}number++;System.out.print(Thread.currentThread().getName()+"::"+number+"--->");// 唤醒其他线程this.notifyAll();}// 实现-1操作public synchronized void decr() throws InterruptedException {// 操作:判断、干活、通知if (number != 1) {// number不为0,等待this.wait();}number--;System.out.println(Thread.currentThread().getName()+"::"+number);this.notifyAll();}
}
public class InterThreadCommunication {public static void main(String[] args) {Share share = new Share();new Thread(new Runnable() {@Overridepublic void run() {try {for (int i = 0; i < 100; i++) {share.incr();}} catch (InterruptedException e) {e.printStackTrace();}}},"A").start();new Thread(new Runnable() {@Overridepublic void run() {try {for (int i = 0; i < 100; i++) {share.decr();}} catch (InterruptedException e) {e.printStackTrace();}}},"B").start();new Thread(new Runnable() {@Overridepublic void run() {try {for (int i = 0; i < 100; i++) {share.incr();}} catch (InterruptedException e) {e.printStackTrace();}}},"C").start();new Thread(new Runnable() {@Overridepublic void run() {try {for (int i = 0; i < 100; i++) {share.decr();}} catch (InterruptedException e) {e.printStackTrace();}}},"D").start();}
}

输出结尾如下,显然最后输出的结果和我们预想的是不一样的。那么问题出在哪里呢?

查找 JDK1.8 文档,在 Objectwait() 方法中有如下介绍

在一个参数版本中,中断和虚假唤醒是可能的,并且该方法应该始终在循环中使用

也就是说,这种现象叫做【虚假唤醒】。所谓虚假唤醒,就是 wait()方法的一个特点,总结来说 wait() 方法使线程在哪里睡就在哪里醒。 这是什么意思呢?那就以上述代码为例。

当 A 进入临界区,BCD三个线程在 if 判断后进入 wait() 等待,当A线程完成操作,此时 number 值为1,notifyAll() 会随机唤醒一个线程。

现在C被唤醒,由于 wait() 方法使线程在哪里睡就在哪里醒,所以接下来C在执行时不会再通过 if 判断而是直接+1,此时 number 就是2了。从而导致最后输出的结果和我们预想的不一致。

按照 JDK1.8 文档的提示,将资源类的 incr() 方法和 decr() 方法中的if语句改为循环语句,修改代码如下:

// 创建一个资源类
class Share{// 设置临界资源private int number = 0;// 实现+1操作public synchronized void incr() throws InterruptedException {// 操作:判断、干活、通知while (number != 0) {// number不为0,等待// 哪里睡哪里起this.wait();}number++;System.out.print(Thread.currentThread().getName()+"::"+number+"--->");// 唤醒其他线程this.notifyAll();}// 实现-1操作public synchronized void decr() throws InterruptedException {// 操作:判断、干活、通知while (number != 1) {// number不为0,等待this.wait();}number--;System.out.println(Thread.currentThread().getName()+"::"+number);this.notifyAll();}
}

此时输出结果符合我们预期:

3.3 Lock 实现案例

Lock 接口中,有一个 newCondition() 方法,该方法返回一个新 Condition 绑定到该实例 Lock 实例。

Condition 类中有 await()signalAll() 等方法,和 synchronized 实现案例中的 wait()notifyAll() 方法相同。所以通过 Lock 接口创建一个 Condition 对象,由该对象的方法进行等待和唤醒操作

实例代码如下,主要改动的是资源类,main方法中代码不变。

class Share {// 设置临界资源private int number = 0;// 创建一个Comprivate Lock lock = new ReentrantLock();private Condition condition = lock.newCondition();// 实现+1操作public void incr() {// 上锁lock.lock();try {// 判断while (number != 0) {condition.await();}// 干活number++;System.out.print(Thread.currentThread().getName() + "::" + number + "--->");// 通知condition.signalAll();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}// 实现-1操作public void decr() throws InterruptedException {// 上锁lock.lock();try {// 判断while (number != 1) {condition.await();}// 干活number--;System.out.println(Thread.currentThread().getName() + "::" + number);// 通知condition.signalAll();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}
}

测试结尾如下:

【JUC并发编程03】线程间通信相关推荐

  1. 并发编程-03线程安全性之原子性(Atomic包)及原理分析

    文章目录 线程安全性文章索引 脑图 线程安全性的定义 线程安全性的体现 原子性 使用AtomicInteger改造线程不安全的变量 incrementAndGet源码分析-UnSafe类 compar ...

  2. 19、Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  3. java面试-Java并发编程(六)——线程间的通信

    多条线程之间有时需要数据交互,下面介绍五种线程间数据交互的方式,他们的使用场景各有不同. 1. volatile.synchronized关键字 PS:关于volatile的详细介绍请移步至:Java ...

  4. C++多线程编程分析-线程间通信

    上文我们介绍了如何建立一个简单的多线程程序,多线程之间不可避免的需要进行通信.相比于进程间通信来说,线程间通信无疑是相对比较简单的. 首先我们来看看最简单的方法,那就是使用全局变量(静态变量也可以)来 ...

  5. 【JUC】第二章 线程间通信、集合的线程安全

    第二章 线程间通信.集合的线程安全 文章目录 第二章 线程间通信.集合的线程安全 一.线程间通信 1.介绍 2.synchronized 方案 3.Lock 方案 4.定制化线程通信 二.集合的线程安 ...

  6. java判断线程是否wait_Java并发编程之线程间通讯(上)wait/notify机制

    线程间通信 如果一个线程从头到尾执行完也不和别的线程打交道的话,那就不会有各种安全性问题了.但是协作越来越成为社会发展的大势,一个大任务拆成若干个小任务之后,各个小任务之间可能也需要相互协作最终才能执 ...

  7. python多线程编程(7):线程间通信

    From: http://www.cnblogs.com/holbrook/archive/2012/03/21/2409031.html 很多时候,线程之间会有互相通信的需要.常见的情形是次要线程为 ...

  8. 线程join_Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)

    点击上方"Coder编程",选择"置顶公众号" 技术文章第一时间送达! 并发编程.png 每天进步一点,不做curd工程师与Api调用工程师 欢迎访问 个人博客 ...

  9. python线程通信 消息传递_Python并发编程之线程消息通信机制/任务协调(四)

    大家好,并发编程进入第四篇. 本文目录 前言 Event事件 Condition Queue队列 总结 .前言 前面我已经向大家介绍了,如何使用创建线程,启动线程.相信大家都会有这样一个想法,线程无非 ...

最新文章

  1. 灵活运用ISA的链接转换功能:ISA2006系列之十三
  2. 安卓项目之微信公众好---初体验
  3. web开发应届生入职_我如何从全职妈妈着手完成第一份Web开发人员工作
  4. jmeter接口测试----8用户定义的变量
  5. Java面试之设计模式七大原则
  6. Scala : unsupported operationexception : empty.reduceLeft
  7. LeetCode10 Regular Expression Matching
  8. php网站程序更新功能,运用PHP定时自动更新网站首页HTML的方法
  9. 人人商城人人店人人分销商城V2.8.0解密开源版,收银台+秒杀+区域代理+积分商城+多商户
  10. 单服务器高性能:PPC、TPC、epoll、Reactor、Proactor
  11. Tomcat多层容器的设计
  12. 计算机函数sumif怎么用,sumif函数的使用方法_EXCEL的经典函数sumif的用法和实例(详细汇总)...
  13. 网页设计课设【登录注册系统及增删改查】
  14. 11月最新非主流男生混搭头像一组_我的爱不再能分给你
  15. 离散数学 传递闭包 Warshall算法
  16. Sequence Model-week1编程题3-用LSTM网络生成爵士乐
  17. 魔法宝石 spfa
  18. 少说话多写代码之Python学习048——类的成员(supper函数)
  19. 快手公布于香港联交所主板上市计划详情;木莲庄酒管全线开放加盟合作 | 美通企业日报...
  20. Windows Phone 7 小应用 生理节律表

热门文章

  1. Perl函数pack/unpack(二进制读写)
  2. 错误 C2664 “int WideCharToMultiByte......”: 无法将参数 3 从“CString”转换为“LPCWCH” 的问题解决
  3. Linux内核中断底半部处理--工作队列
  4. C++ Primer 5th笔记(chap 17 标准库特殊设施)tuple 返回多个值
  5. java练气期(1)----java高级(JDBC)
  6. 904. 水果成篮(滑动窗口)模板题
  7. web自动化测试常见面试题
  8. [HOW TO]-github/gitee私有项目用户名密码免输入
  9. Docker Compose基本介绍
  10. WIN32 使用 MUTEX 实现禁止多开