何为虚假唤醒:

当一个条件满足时,很多线程都被唤醒了,但是只有其中部分是有用的唤醒,其它的唤醒都是无用功;
比如买货:如果商品本来没有货物,突然进了一件商品,这是所有的线程都被唤醒了,但是只能一个人买,所以其他人都是假唤醒,获取不到对象的锁;

避免虚假唤醒:

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并发编程虚假唤醒问题(生产者和消费者关系)的更多相关文章

  1. JAVA并发框架之Semaphore实现生产者与消费者模型

    分类: Java技术      锁和信号量(Semaphore)是实现多线程同步的两种常用的手段.信号量需要初始化一个许可值,许可值可以大于0,也可以小于0,也可以等于0.      如果大于0,表示 ...

  2. Java并发编程之美之并发编程线程基础

    什么是线程 进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是进程的一个执行路径,一个进程至少有一个线程,进程的多个线程共享进程的资源. java启动main函数其实就 ...

  3. 【Java并发编程实战】----- AQS(三):阻塞、唤醒:LockSupport

    在上篇博客([Java并发编程实战]----- AQS(二):获取锁.释放锁)中提到,当一个线程加入到CLH队列中时,如果不是头节点是需要判断该节点是否需要挂起:在释放锁后,需要唤醒该线程的继任节点 ...

  4. Java 并发编程 生产者消费者模式

    本文部分摘自<Java 并发编程的艺术> 模式概述 在线程的世界里,生产者就是生产数据的线程,消费者就是消费数据的数据.生产者和消费者彼此之间不直接通信,而是通过阻塞队列进行通信,所以生产 ...

  5. java并发编程之美-阅读记录1

    1.1什么是线程? 在理解线程之前先要明白什么是进程,因为线程是进程中的一个实体.(线程是不会独立存在的) 进程:是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是进程中的 ...

  6. Java并发编程(您不知道的线程池操作), 最受欢迎的 8 位 Java 大师,Java并发包中的同步队列SynchronousQueue实现原理

    Java_并发编程培训 java并发程序设计教程 JUC Exchanger 一.概述 Exchanger 可以在对中对元素进行配对和交换的线程的同步点.每个线程将条目上的某个方法呈现给 exchan ...

  7. Java并发编程--基础进阶高级(完结)

    Java并发编程--基础进阶高级完整笔记. 这都不知道是第几次刷狂神的JUC并发编程了,从第一次的迷茫到现在比较清晰,算是个大进步了,之前JUC笔记不见了,重新做一套笔记. 参考链接:https:// ...

  8. JAVA并发编程J.U.C学习总结

    前言 学习了一段时间J.U.C,打算做个小结,个人感觉总结还是非常重要,要不然总感觉知识点零零散散的. 有错误也欢迎指正,大家共同进步: 另外,转载请注明链接,写篇文章不容易啊,http://www. ...

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

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

随机推荐

  1. 一个接口多个实现Spring如何实现正确注入

    阅读源码的时候,时不时脑子中冒出一个问题:如果自己写一个单例的容器,一个接口多个实现怎么正确地将每个实现都缓存容器中并准确地注入呢?阅读Dubbo源码时也有类似的问题冒出来. 对于@Autowire注 ...

  2. java实现 TCP通信

    //服务端import com.hl.bluetooth.util.CRC16; import com.hl.bluetooth.util.FrameCheckFailedException; imp ...

  3. CobaltStrike逆向学习系列(14):CS功能分析-DotNet

    这是[信安成长计划]的第 14 篇文章 0x00 目录 0x01 DotNet功能分析 0x02 DotNet功能执行 0x03 写在最后 在上两篇文章中,讲述了 CS 中的一种功能执行方式 RDI, ...

  4. TestNG基本使用

    TestNG简介 Testng是一套开源测试框架,是从Junit继承而来,testng意为test next generation 创建maven项目,添加依赖 <dependency> ...

  5. css文字超出指定行数显示省略号

    display: -webkit-box; overflow: hidden; word-break: break-all; /* break-all(允许在单词内换行.) */ text-overf ...

  6. c# 编程学习(三)

    方法是具名的语句序列 returnType methodName ( parameterList ) {  // 这里添加方法主体语句 } returnType(返回类型)是类型名称,指定方法返回的数 ...

  7. 【C# 线程】RPC中常见的Stub| marshalling怎么理解

    RPC服务的基本架构图如上,可以很清楚地看到,一个完整的RPC架构里面包含了四个核心的组件,分别是Client ,Server,Client Stub以及Server Stub,这个Stub大家可以理 ...

  8. Shell、命令行界面、控制台什么区别

    Shell 是什么?Shell 是一个命令解释器,它为用户提供了一个向 操作系统内核发送请求以便运行程序界面系统级程序,它的作用就是遵循一定的语法将输入的命令加以解释并传给系统,他大意是指对系统的操控 ...

  9. oracle 11g rac修改监听端口(远程监听和本地监听)

    转至:https://www.cnblogs.com/yj411511/p/12459533.html 目录 1.修改远程监听端口 1.1 查看远程监听状态 1.2 修改SCAN listener端口 ...

  10. 修改Ehcache缓存中取到的值,缓存中的值也被修改了

    问题现象 我们从Ehcache中取出缓存的对象,之后将对象中的属性进行了修改使用.等再次从缓存中拿到对象后,发现对象的值变成了上一次调用修改后的对象了. 原因 Ehcache中缓存的是原对象的引用,所 ...