java八股文面试[多线程]——虚假唤醒

news2025/1/19 19:15:55

阻塞队列中,如果需要线程挂起操作,判断有无数据的位置采用的是while循环 ,为什么不能换成if

肯定是不能换成if逻辑判断

线程A,线程B,线程E,线程C。 其中ABE生产者,C属于消费者

put阻塞代码:

//put方法,阻塞时可中断
 public void put(E e) throws InterruptedException {
     checkNotNull(e);
      final ReentrantLock lock = this.lock;
      lock.lockInterruptibly();//该方法可中断
      try {
          //当队列元素个数与数组长度相等时,无法添加元素
          while (count == items.length)
              //将当前调用线程挂起,添加到notFull条件队列中等待唤醒
              notFull.await();
          enqueue(e);//如果队列没有满直接添加。。
      } finally {
          lock.unlock();
      }
  }
 

假如线程的队列是满的,会进行阻塞,待加入的元素还未执行,需要等一个空位置

// E,拿到锁资源,还没有走while判断
while (count == items.length)
    // A醒了
    // B挂起
    notFull.await();
enqueue(e);

C此时消费一条数据,执行notFull.signal()唤醒一个线程A线程被唤醒(A是因为等待空位置进行的阻塞)

E这时刚好需要生产一个对象,走判断,发现有空余位置(A还没有加入元素),可以添加数据到队列,E添加数据,走enqueue,这个时候队列满了。如果判断是if(notFull.await 只执行一次)A在E释放锁资源后,拿到锁资源,直接走enqueue方法。此时A线程就是在putIndex的位置,覆盖掉之前E加入的的数据,造成数据安全问题

本质原因是,在生产者从被阻塞唤醒时,其他生产者可以能已经加了一个元素,导致队列满了。

直接看while和if的区别

可以看出来,if 是true情况下,if内的代码也只会执行一次,而while直至不满足条件才能跳出while循环

先分析while情况下
public class Demo {
    public static void main(String[] args) {
        Products data = new Products();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        },"A1").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.decrement();
            }
        },"B1").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        },"A2").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.decrement();
            }
        },"B2").start();
    }
}

class Products{
    private int number = 0;

    public synchronized void increment(){
        while (number !=0){
            try {
                System.out.println(Thread.currentThread().getName()+"商品充足,当前商品数量为"+number);
                System.out.println(Thread.currentThread().getName()+"wait前");
                this.wait();
                System.out.println(Thread.currentThread().getName()+"wait后");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"数据加一 !===>" + number);
        this.notifyAll();
    }

    public synchronized void decrement(){
        while (number == 0){
            try {
                System.out.println(Thread.currentThread().getName()+"商品不足,当前商品数量为"+number);
                System.out.println(Thread.currentThread().getName()+"wait前");
                this.wait();
                System.out.println(Thread.currentThread().getName()+"wait后");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"数据减一 !===>" + number);
        this.notifyAll();
    }
}

创建了两个生产者和两个消费者

可以看出来,当前线程wait后,之后就会唤醒其他线程,获取到CPU的线程后再执行wait后面一行代码,可以得出结论 wait后 该线程代码停留在此点,并释放锁,等待唤醒后就会继续执行wait后的代码 (wait是立马释放当前锁,进入阻塞状态,notify是执行完当前代码块后面的代码再释放锁)

由此可以根据if和while的特性推导

​如果是while,唤醒后,把wait后面循环体代码执行完后,会判断while是否为true,只有不满足才能跳出while

​如果是if,唤醒后,会直接顺着wait后面的代码执行下去,不会考虑if是否为true

虚假唤醒:当线程从条件变量中苏醒过来时,发现等待的条件并没有满足,该Demo产生的情况是使用if,生产者A1执行完后,唤醒其他线程,获取到的CPU却是生产者A2,A2进入if中后,并没有满足条件,但是由于是if,会顺着执行再次生产一个,消费者同理。
 

知识来源:

虚假唤醒分析_落月飞雪的博客-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/967055.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UDP聊天室

1.头文件 /* * 文件名称&#xff1a;UDP.h * 创 建 者&#xff1a;crx * 创建日期&#xff1a;2023年09月3日 * 描 述&#xff1a; */ #ifndef _UDP_H #define _UDP_H#include <stdio.h> #include <sys/types.h> /* See NOTES */ #includ…

Day53|leetcode 1143.最长公共子序列、1035.不相交的线、53. 最大子序和

leetcode 1143.最长公共子序列 题目链接&#xff1a;1143. 最长公共子序列 - 力扣&#xff08;LeetCode&#xff09; 视频链接&#xff1a;动态规划子序列问题经典题目 | LeetCode&#xff1a;1143.最长公共子序列_哔哩哔哩_bilibili 题目概述 给定两个字符串 text1 和 text2&…

【Flutter】Flutter 使用 percent_indicator 实现基于百分比显示进度

【Flutter】Flutter 使用 percent_indicator 实现基于百分比显示进度 文章目录 一、前言二、安装和基本使用三、圆形百分比指示器四、线性百分比指示器五、完整示例六、总结 一、前言 今天我要为你介绍一个非常实用的Flutter包——percent_indicator。这个包允许我们基于百分比…

STM32f103入门(9)编码器接口测速

TIM3 PA6 PA7 上拉输入 原理上也是PWM捕获输入 捕获两个输入 我们用中断处理读取CNT的值 读取完将CNT置0 这样我们就得到了旋转编码器的速度/s 中断配置代码 #include "stm32f10x.h" // Device headervoid Timer_Init(void) {RCC_APB1PeriphClockC…

【Linux-Day9-进程间通信】

进程间通信 前提引入&#xff1a; 我们之前接触过管道 | 将前一个命令的输出结果作为后一个命令的输入 如&#xff1a;ps | grep “sleep” 其实就是将 ps 的结果写入到一个区域&#xff0c;在从这个区域读出目标数据 有名管道 mkfifo 创建管道文件 : mkfifo fifo ->创…

verilator——牛刀小试

verilator——牛刀小试 安装verilator可见&#xff1a;https://blog.csdn.net/qq_40676869/article/details/132648522?spm1001.2014.3001.5501 正文开始 编写一个异或的电路模块如下&#xff1a; top.v module top(input a,input b,output f );assign f a ^ b; endmodul…

[华为云云服务器评测] 华为云耀云服务器 Java、node环境配置

系列文章目录 第一章 [linux实战] 华为云耀云服务器L实例 Java、node环境配置 文章目录 系列文章目录前言一、任务拆解二、修改密码三、配置安全规则四、远程登录并更新apt五、安装、配置JDK环境5.1、安装openjdk,选择8版本5.2、检查jdk配置 六、安装、配置git6.1、安装git6.2…

【从0学习Solidity】合约入门 Hello Web3

【学习Solidity的基础】入门智能合约开发 Hello Web3 &#x1f4f1;不写代码没饭吃上架主页 在强者的眼中&#xff0c;没有最好&#xff0c;只有更好。我们是全栈开发领域的优质创作者&#xff0c;同时也是阿里云专家博主。 ✨ 关注我们的主页&#xff0c;探索全栈开发的无限…

torch.bmm功能解读

bmm 是 batch matrix multiple 的简写&#xff0c;即批量矩阵乘法&#xff0c;矩阵是二维的&#xff0c;加上batch一个维度&#xff0c;因此该函数的输入必须是两个三维的 tensor&#xff0c;三个维度代表的含义分别是&#xff1a;&#xff08;批量&#xff0c;行&#xff0c;列…

递归与递推

会独立敲一遍代码并debug&#xff08;1&#xff0c;3题较难&#xff1b;2&#xff0c;4题较简单&#xff09; 部分题需要买课&#xff0c;可到洛谷或其他OJ找原题 目录 &#x1f4d5;空间复杂度(计算方法) &#x1f33c;1&#xff0c;费解的开关 &#x1f33c;2&#xff0c;…

图神经网络教程之HAN-异构图模型

异构图 包含不同类型节点和链接的异构图 异构图的定义&#xff1a;节点类别数量和边的类别数量加起来大于2就叫异构图。 meta-path元路径的定义&#xff1a;连接两个对象的复合关系&#xff0c;比如&#xff0c;节点类型A和节点类型B&#xff0c;A-B-A和B-A-B都是一种元路径。 …

[C++] STL_list常用接口的模拟实现

文章目录 1、list的介绍与使用1.1 list的介绍1.2 list的使用 2、list迭代器3、list的构造4、list常用接口的实现4.1 list capacity4.2 插入删除、交换、清理4.2.1 insert任意位置插入4.2.2 push_front头插4.2.3 push_back尾插4.2.4 erase任意位置删除4.2.5 pop_front头删4.2.6 …

Keil 编译 Debug

# 头文件无法导入进来 # 导入头文件&#xff0c;只有函数声明&#xff0c;但缺少函数实现 已经导入了air32f10x_gpio.h但是没有导入 .c&#xff0c;就导致 编译出错出现undefined symbol (某个函数)&#xff0c;这时候按照下面的操作&#xff0c;导入外设模块就好。

PQUEUE - Printer Queue

题目描述 The only printer in the computer science students union is experiencing an extremely heavy workload. Sometimes there are a hundred jobs in the printer queue and you may have to wait for hours to get a single page of output. Because some jobs are …

pip切换源

pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

CLFS信息泄露漏洞CVE-2023-28266分析

引用 这篇文章的目的是介绍今年4月发布的CLFS信息泄露漏洞CVE-2023-28266分析. 文章目录 引用简介CVE-2023-28266漏洞分析CVE-2023-28266调试过程漏洞复现相关引用参与贡献 简介 文章结合了逆向代码和调试结果分析了CVE-2023-28266漏洞利用过程和漏洞成因. CVE-2023-28266漏洞…

两个线程同步执行:解决乱箭穿心(STL/Windows/Linux)

C自学精简教程 目录(必读) C并发编程入门 目录 多线程同步 线程之间同步是指线程等待其他线程执行完某个动作之后再执行&#xff08;本文情况&#xff09;。 线程同步还可以是像十字路口的红绿灯一样&#xff0c;只允许一个方向的车同行&#xff0c;其他方向的车等待。 本…

UART串口Shell软硬件模型分析总结

文章目录 层次一、最底层逻辑配置交互----如何从Uart硬件读写单个字节数据层次二、抽象串口软件模块交互----基于串口对接输入输出流 和 Printf适配层次三、类似Shell封装抽象交互----基于串口交互命令行界面&#xff08;命令解析、补全、修改、记录&#xff09;case1 依次输入…

自建音乐服务器Navidrome之一

这里写自定义目录标题 1.1 官方网站 2. Navidrome 简介2.1 简介2.2 特性 3. 准备工作4. 视频教程5. 界面演示5.1 初始化页5.2 专辑页 前言 之前给大家介绍过 Koel 音频流服务&#xff0c;就是为了解决大家的这个问题&#xff1a;下载下来的音乐&#xff0c;只能在本机欣赏&…

上海的正西边有哪些城市

背景 上海一路向西&#xff0c;来一趟拉萨之行&#xff0c;那么上海出现&#xff0c;所经过的那么多城市&#xff0c;哪些是在上海的正西边呢&#xff1f; 画一幅地图 基于这个背景需求&#xff0c;我们需要拿来一幅地图&#xff0c;一看便知。下面的python代码生成了一幅地…