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 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...
随机推荐
- Linux基础:子网划分
一.ip地址基本知识 1.1 ip地址的结构和分类 1.2 特殊ip地址 1.3 子网掩码 1.4 ip地址申请 二.子网划分 2.1 子网划分概念 2.2 c类子网划分初探 2.3 子网划分步骤 2 ...
- 协程 & IO模型 & HTTP协议
今日内容 进程池与线程池的基本使用 协程理论与实操 IO模型 前端简介 内容详细 一.进程池与线程池的基本使用 1.进程池与线程池的作用 为了保证计算机硬件安全的前提下,提升程序的运行效率 2.回调机 ...
- Solution -「CF 1392H」ZS Shuffles Cards
\(\mathcal{Description}\) Link. 打乱的 \(n\) 张编号 \(1\sim n\) 的数字排和 \(m\) 张鬼牌.随机抽牌,若抽到数字,将数字加入集合 \(S ...
- VS2019下配置OpenGL全过程
一:下载VS2019 官网下载社区版 二:下载GLEW.GLFW 百度网盘地址: 链接:https://pan.baidu.com/s/1Uvz9svdnVRvDXNHjVgApig 提取码:rsgp ...
- 私有化轻量级持续集成部署方案--07-私有NPM仓库-Verdaccio
提示:本系列笔记全部存在于 Github, 可以直接在 Github 查看全部笔记 对于个人来说,私有NPM仓库 作用性基本很小,但是对于企业,私有NPM仓库 可以保护代码暴露,具有很大的意义. 也是 ...
- Docker配置Pytorch深度学习环境
拉取镜像 $ docker pull pytorch/pytorch:1.9.1-cuda11.1-cudnn8-devel 查看本地已有镜像 $ docker images 创建容器 $ docke ...
- Vue 源码解读(7)—— Hook Event
前言 Hook Event(钩子事件)相信很多 Vue 开发者都没有使用过,甚至没听过,毕竟 Vue 官方文档中也没有提及. Vue 提供了一些生命周期钩子函数,供开发者在特定的逻辑点添加额外的处理逻 ...
- 【C# Parallel】开端
使用条件 1.必须熟练掌握锁.死锁.task的知识,他是建立这两个的基础上的.task建立在线程和线程池上的. 2.并不是所有代码都适合并行化. 例如,如果某个循环在每次迭代时只执行少量工作,或它在很 ...
- 【.net】AppDoamin| 应用程序域
在.net framework框架上,一个进程可以有多个appdomain.因此一个进程可以运行多个程序. 应用程序域的出现: (来自msdn) 在.net出现以前,一个进程下,只能运行一个应用 ...
- linux下通过脚本方式对中间件weblogic进行补丁升级
转至:http://bbs.learnfuture.com/topic/48 linux下通过脚本方式对中间件weblogic进行补丁升级 刘五奎 [摘要] 在运维行业,系统或软件漏洞每每牵动着每一个 ...