JMM之Java线程间通讯——等待通知机制及其经典范式
在并发编程中,实际处理涉及两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体)。
通信是指线程之间以何种机制来交换信息。在共享内存的并发模型里,线程之间共享程序的公共状态,通过写-读内存中的公共状态进行隐式通信。
同步是指程序中用于控制不同线程间操作发生相对顺序的机制。在共享内存并发模型里,同步是显式进行的。开发人员必须显示指定某个方法或某段代码需要在线程之间互斥执行。
Java的并发选择采用的就是共享内存模型JMM,即隐式通讯显式同步。
在如何在两个线程之间共享数据 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中通过案例浅析了Java线程之间如何实现共享数据的两种方式,同时通过测试结果也可以知道线程之间的执行相对顺序是随机的,需要开发人员进行有效的同步控制。Java中对多线程执行顺序控制利用等待/通知也称为阻塞/唤醒机制实现。
等待/通知的相关方法是任意Java对象都具备的,因为这些方法被定义在所有对象的超类java.lang.Object上:
等待/通知机制,是指一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B调用了对象O的notify()或者notifyAll()方法,线程A收到通知后从对象O的wait()方法返回,进而执行后续操作。上述两个线程通过对象O来完成交互,而对象上的wait()和notify/notifyAll()的关系就如同开关信号一样,用来完成等待方和通知方之间的交互工作。
先看单线程使用该机制:通过实践结果看,生成一个消费一个。
再看多线程:
再执行一次:
多线程每次执行的结果似乎都不一样,也不是先生产后消费。wait和notify在此多线程应用不失效了?!从通知等待机制来分析,多线程的案例中6个线程分两组分别调用了对象myQueue中的wait()和notify(),相互通讯不应该出现失效的情况,为什么还是会出现消费者线程没有等到生产者的通知已生产完毕再进行消费的情况?
分析源代码,通知等待机制哪里失控了?首先分析消费者线程:
从源码看:消费者线程根据随机数获取消息队列中的数据。同时消息队列长度为0时wait(),除此以外都是根据随机数取数据。失控的点就在此处:如果消息队列长度不为0,比如说其长度为2,同时随机数为3,那么消费者线程取队列中的第4个数据——可想而知是没有任何元素在此处的。
综合上述,多线程并发如果只是有关键字synchronized,wait(),notify()等方法并不能完全保证业务逻辑的正确性即有效同步。怎么解决这个问题呢?还需要合理的业务逻辑:
以上可以得出一个小经验:提炼出等待/通知的经典范式——加锁,循环和处理逻辑,该范式分为两部分,分别针对等待方(消费者)和通知方(生产者)。
等待方遵循如下原则:
1)获取对象的锁。
2)如果条件不满足,那么调用对象的wait()方法,被通知后仍要检查条件。
3)条件满足则执行对应的逻辑。
对应的伪代码如下:
synchronized(对象) {
while(条件不满足) {
对象.wait();
}
对应的处理逻辑
}
通知方遵循如下原则:
1)获得对象的锁。
2)改变条件。
3)通知所有等待在对象上的线程。
对应的伪代码如下:
synchronized(对象) {
改变条件
对象.notifyAll();
}
JMM之Java线程间通讯——等待通知机制及其经典范式的更多相关文章
- Java 线程间通信 —— 等待 / 通知机制
本文部分摘自<Java 并发编程的艺术> volatile 和 synchronize 关键字 每个处于运行状态的线程,如果仅仅是孤立地运行,那么它产生的作用很小,如果多个线程能够相互配合 ...
- Java 线程间通讯(共享变量方式)
Java线程间通讯,最常用的方式便是共享变量方式,多个线程共享一个静态变量就可以实现在线程间通讯,但是这需要注意的就是线程同步问题. 一.没考虑线程同步: package com.wyf; publi ...
- Java Concurrency - wait & notify, 等待通知机制
生产者消费者问题是一个常见的多线程同步案例:一组生产者线程和一组消费者线程共享一个初始状态为空.大小为 N 的缓冲区.只有当缓冲区没满的时候,生产者才能把消息放入缓冲区,否则必须等待:只有缓冲区不空的 ...
- Java并发读书笔记:线程通信之等待通知机制
目录 synchronized 与 volatile 等待/通知机制 等待 通知 面试常问的几个问题 sleep方法和wait方法的区别 关于放弃对象监视器 在并发编程中,保证线程同步,从而实现线程之 ...
- Java 线程间通讯
/* 线程间通讯: 多个线程在处理同一资源,但是任务却不同. */ package com.cwcec.test; class Input implements Runnable { Resource ...
- java多线程系列(三)---等待通知机制
等待通知机制 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我的理解 ...
- Java 线程间通讯(管道流方式)
一.管道流是JAVA中线程通讯的常用方式之一,基本流程如下: 1)创建管道输出流PipedOutputStream pos和管道输入流PipedInputStream pis 2)将pos和pis匹配 ...
- Java并发编程,Condition的await和signal等待通知机制
Condition简介 Object类是Java中所有类的父类, 在线程间实现通信的往往会应用到Object的几个方法: wait(),wait(long timeout),wait(long tim ...
- 12.详解Condition的await和signal等待通知机制
1.Condition简介 任何一个java对象都天然继承于Object类,在线程间实现通信的往往会应用到Object的几个方法,比如wait(),wait(long timeout),wait(lo ...
随机推荐
- Numpy实现简单BP神经网络识别手写数字
本文将用Numpy实现简单BP神经网络完成对手写数字图片的识别,数据集为42000张带标签的28x28像素手写数字图像.在计算机完成对手写数字图片的识别过程中,代表图片的28x28=764个像素的特征 ...
- HTML、CSS、Javascript、jQuery、Xml
HTML HTML简介 Hyper Text Markup Language (超文本标记语言)简写:HTML.通过标签来标记要显示的网页中的各个部分.网页文件本身是一种文本文件,通过在文本文件中添加 ...
- hdfs文件导入到hive(带资源)
前言 hive是基于Hadoop的一个数据仓库工具,用来进行数据提取.转化.加载,这是一种可以存储.查询和分析存储在Hadoop中的大规模数据的机制.hive数据仓库工具能将结构化的数据文件映射为一张 ...
- mysql数据库优化1
目录 数据库结构的设计优化 1.数据库结构的设计 2.针对大型的数据量提前进行分库和分表 3.分库分表带来的问题 4.表结构设计注意的问题 查询优化 1.查询语句的注意事项 2.应尽量避免在 wher ...
- vue3路由的使用,保证你有所收获!
路由变量 有的小伙伴,可能是第一次听见路由变量这个词. 什么是路由变量了,顾名思义就是这个路由地址是动态的,不是固定的. 它的运用场景是哪里呢? 比如说:1.详情页的地址,我们就可以去使用路由变量. ...
- 【小记录】解决链接libcufft_static.a库出现的错误
程序中使用了 cv::cuda::dft() 函数,需要在链接的时候使用libcufft_static.a这个库.链接出现大量类似错误:error: undefined reference to __ ...
- 面试突击17:HashMap除了死循环还有什么问题?
面试合集:https://gitee.com/mydb/interview 本篇的这个问题是一个开放性问题,HashMap 除了死循环之外,还有其他什么问题?总体来说 HashMap 的所有" ...
- 了解promise、promise对象
Promise 是异步编程的一种解决方案,比传统的解决方案--回调函数和事件--更合理和更强大.它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象. 所谓Pr ...
- python利用正则表达式提取文本中特定内容
正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配. Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式. re 模块使 Python ...
- Android Studio 插件(不定期更新)
GsonFormat 根据JSONObject格式的字符串,自动生成实体类参数. 安装 1.Android studio File->Settings-->Plugins -->in ...