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 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...
随机推荐
- Solution -「多校联训」签到题
\(\mathcal{Description}\) Link. 给定二分图 \(G=(X\cup Y,E)\),求对于边的一个染色 \(f:E\rightarrow\{1,2,\dots,c\ ...
- Dump Lsass内存转储新旧方法
之前看到一篇关于Lsass内存dump的文章,学习记录一下. lsass.exe(Local Security Authority Subsystem Service)进程空间中,存有着机器的 ...
- 暑假撸系统3- petty热更新 mybatis自动填充时间字段!
经过了昨天纠结技术选型,和一大堆xml配置,终于把架子搭好了.因为最近一次做java项目也在好多年以前了(毕竟用了pytohn以后谁也不想再回来java了),java的生态发生了长足的进步,本来想从原 ...
- 《从零开始, 开发一个 Web Office 套件》系列博客目录
这是一个系列博客, 最终目的是要做一个基于HTML Canvas 的, 类似于微软 Office 的 Web Office 套件, 包括: 文档, 表格, 幻灯片... 等等. 对应的Github r ...
- Linux性能优化概述
一.Linux性能概述 性能优化是个系统工程,总是牵一发而动全身,它涉及了从程序设计.编程语言,再到系统.存储.网络等各种底层基础设施的方方面面.每一个组件都有可能出问题,而且很有可能多个组件同时出问 ...
- CentOS8安装启用telnet服务
CentOS8默认只安装telnet的客户端,其服务端telnet-server由于安全原因由用户自主选择安装.下面为实践安装启动telnet-server,注意其与vsftp服务安装的异同. 1.查 ...
- Ubuntu20重装nvidia驱动
终端:nvidia-smi 查看驱动信息 错误:NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver ...
- kali linux清理垃圾
sudo apt-get autoclean # 清理旧版本的软件缓存 sudo apt-get clean # 清理所有软件缓 ...
- SRv6规模部署,离不开测试技术保驾护航!
什么是SRv6? SRv6技术就是采用现有的IPv6转发技术,通过扩展IPv6报文的头域,实现类似标签转发的处理.SRv6将一些IPv6地址定义成实例化的SID(Segment ID),每个SID有着 ...
- Excel数据可视化图表设计需要注意的几个问题
大数据发展迅速的时代,数据分析驱动商业决策.对于庞大.无序.复杂的数据要是没经过合适的处理,价值就无法体现. 可以想象一本没有图片的教科书.没有图表.图形或是带有箭头和标签的插图或流程图,那么这门学 ...