Java并发编程虚假唤醒问题(生产者和消费者关系)
何为虚假唤醒:
当一个条件满足时,很多线程都被唤醒了,但是只有其中部分是有用的唤醒,其它的唤醒都是无用功;
比如买货:如果商品本来没有货物,突然进了一件商品,这是所有的线程都被唤醒了,但是只能一个人买,所以其他人都是假唤醒,获取不到对象的锁;
避免虚假唤醒:
Synchronized版,生产者和消费者问题
package com.jia.pc;
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10 ; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10 ; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10 ; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10 ; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
// 等待,业务,通知
class Data{
private int number = 0;
// +1
public synchronized void increment() throws InterruptedException {
while (number != 0){
// 等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "->" + number);
// 通知其他线程,我+1完毕
this.notifyAll();
}
// -1
public synchronized void decrement() throws InterruptedException {
while (number == 0){
// 等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "->" + number);
// 通知其他线程,我-1完毕
this.notifyAll();
}
}
运行结果:
A->1
B->0
A->1
B->0
A->1
B->0
A->1
B->0
A->1
B->0
A->1
B->0
A->1
B->0
A->1
B->0
A->1
B->0
A->1
B->0
C->1
D->0
C->1
D->0
C->1
D->0
C->1
D->0
C->1
D->0
C->1
D->0
C->1
D->0
C->1
D->0
C->1
D->0
C->1
D->0 Process finished with exit code 0
虚假幻想是如何产生的?
把 while (number != 0) {}
换成 if (number == 0) {}
就会出现虚假唤醒。官方文档有标注;
为什么if判断会出现虚假唤醒?
1. 因为if只会执行一次,执行完会接着向下执行if()外边的
2. 而while不会,直到条件满足才会向下执行while()外边的
JUC版,生产者和消费者问题
使用 Condition 代码实现:
package com.jia.pc; import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class C { public static void main(String[] args) { Data3 data = new Data3(); new Thread(()->{
for (int i = 0; i < 10 ; i++) {
data.printA();
}
},"A").start(); new Thread(()->{
for (int i = 0; i < 10 ; i++) {
data.printB();
}
},"B").start(); new Thread(()->{
for (int i = 0; i < 10 ; i++) {
data.printC();
}
},"C").start();
}
} // 资源类
class Data3{ private Lock lock = new ReentrantLock(); Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition(); private int number = 1; public void printA(){
lock.lock();
try {
while (number != 1){
//等待
conditionA.await();
}
System.out.println(Thread.currentThread().getName()+"->"+"AAAAA");
//唤醒执行的线程 B
number = 2;
conditionB.signal();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
} public void printB(){
lock.lock();
try {
while (number != 2){
//等待
conditionB.await();
}
System.out.println(Thread.currentThread().getName()+"->"+"BBBBB");
//唤醒 C
number = 3;
conditionC.signal();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
} public void printC(){
lock.lock();
try {
while (number != 3){
//等待
conditionC.await();
}
System.out.println(Thread.currentThread().getName()+"->"+"CCCCC");
//唤醒 A
number = 1;
conditionA.signal();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
Condition:它可以精准的通知和唤醒线程;
Java并发编程虚假唤醒问题(生产者和消费者关系)的更多相关文章
- JAVA并发框架之Semaphore实现生产者与消费者模型
分类: Java技术 锁和信号量(Semaphore)是实现多线程同步的两种常用的手段.信号量需要初始化一个许可值,许可值可以大于0,也可以小于0,也可以等于0. 如果大于0,表示 ...
- Java并发编程之美之并发编程线程基础
什么是线程 进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是进程的一个执行路径,一个进程至少有一个线程,进程的多个线程共享进程的资源. java启动main函数其实就 ...
- 【Java并发编程实战】----- AQS(三):阻塞、唤醒:LockSupport
在上篇博客([Java并发编程实战]----- AQS(二):获取锁.释放锁)中提到,当一个线程加入到CLH队列中时,如果不是头节点是需要判断该节点是否需要挂起:在释放锁后,需要唤醒该线程的继任节点 ...
- Java 并发编程 生产者消费者模式
本文部分摘自<Java 并发编程的艺术> 模式概述 在线程的世界里,生产者就是生产数据的线程,消费者就是消费数据的数据.生产者和消费者彼此之间不直接通信,而是通过阻塞队列进行通信,所以生产 ...
- java并发编程之美-阅读记录1
1.1什么是线程? 在理解线程之前先要明白什么是进程,因为线程是进程中的一个实体.(线程是不会独立存在的) 进程:是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是进程中的 ...
- Java并发编程(您不知道的线程池操作), 最受欢迎的 8 位 Java 大师,Java并发包中的同步队列SynchronousQueue实现原理
Java_并发编程培训 java并发程序设计教程 JUC Exchanger 一.概述 Exchanger 可以在对中对元素进行配对和交换的线程的同步点.每个线程将条目上的某个方法呈现给 exchan ...
- Java并发编程--基础进阶高级(完结)
Java并发编程--基础进阶高级完整笔记. 这都不知道是第几次刷狂神的JUC并发编程了,从第一次的迷茫到现在比较清晰,算是个大进步了,之前JUC笔记不见了,重新做一套笔记. 参考链接:https:// ...
- JAVA并发编程J.U.C学习总结
前言 学习了一段时间J.U.C,打算做个小结,个人感觉总结还是非常重要,要不然总感觉知识点零零散散的. 有错误也欢迎指正,大家共同进步: 另外,转载请注明链接,写篇文章不容易啊,http://www. ...
- Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition
Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...
随机推荐
- 274-基于XC7V690T的3U VPX信号处理板
一.板卡概述 本板卡系我司自主研发的基于3U VPX导冷架构的信号处理板,适用于高速图像处理,雷达信号处理等.芯片采用工业级设计.该处理板包含1片Xilinx公司的Virtex7系列FPGA-XC7V ...
- Solution -「LOJ #6485」 LJJ 学二项式定理
\(\mathcal{Description}\) Link. 给定 \(n,s,a_0,a_1,a_2,a_3\),求: \[\sum_{i=0}^n\binom{n}is^ia_{i\bm ...
- 我们一起来学grep
文章目录 grep 介绍 grep 命令格式 grep 命令选项 grep 实例 查找指定进程 查找指定进程个数 从文件中读取关键词进行搜索 从多个文件中查找关键字 输出以u开头的行 输出非u开头的行 ...
- kube-scheduler源码分析(1)-初始化与启动分析
kube-scheduler源码分析(1)-初始化与启动分析 kube-scheduler简介 kube-scheduler组件是kubernetes中的核心组件之一,主要负责pod资源对象的调度工作 ...
- 带你掌握Java各种日志框架
一:日志基本概念及框架 1:什么是日志 Java程序员在开发项目时都是依赖Eclipse/IDEA等集成开发工具的Debug调试功能来跟踪解决Bug,但项目打包部署发布到了测试环境和生产环境怎么办?难 ...
- unittest测试框架,HTMLTestReportCN模块生成的测试报告中展示用例说明的配置方法
1.前言 想要生成的html测试报告中展示每个测试用例的说明信息,方便了解测试案例的测试点或者其他信息,目前知道的有2种 2.方法介绍 * 方法1: 要添加说明的测试用例,将说明信息用3个引号包裹起来 ...
- 思科VTP协议(后面有配置案例)
一.VTP相关理论介绍 1.1 VTP(VLAN trunking protocol)协议是用来在整个交换网络中分发和同步VLAN数据库的,是一个二层协议,思科私有协议. 1.2 VTP域是由一台或者 ...
- 8款国内外主流商业智能BI工具分析,助你轻松选型!
BI(Business Intelligence),即商业智能或商务智能,它是一套完整的解决方案,用来将企业中现有的数据进行有效的整合,快速准确的提供报表并提出决策依据,帮助企业做出明智的业务经营决策 ...
- 如何在Excel里安装excel插件?
随着科技的发展,人们对数据分析的要求越来越多, Excel也存在一些问题,长期困扰一线业务用户:首先是性能问题.对于大数据量,Excel处理起来很慢.数据获取的过程麻烦,特别是周期性的数据获取,每次都 ...
- 带你掌握Redis数据类型:string和Hash
摘要:Redis中有五大数据类型,分别是String.List.Set.Hash和Zset. 本文分享自华为云社区<Redis的string类型常用命令解析>,作者:灰小猿 . 先问大家一 ...