【多线程】线程间通信 之虚假唤醒和中断

2024-09-02 19:12

本文主要是介绍【多线程】线程间通信 之虚假唤醒和中断,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

两个线程,可以操作初始值为0的一个变量,实现一个线程对该变量+1,一个线程对该变量-1,实现交替,来10轮,变量初始值为0,以实现此问题作为引入,简化我们的理解

文章目录

    • 一、两个线程synchronized写法-结果无问题
    • 二(一)、四个线程synchronized写法-问题及解决办法
    • 二(二)、4线程问题 解决办法1:使用while进行条件判断
    • 二(三)、4线程问题 解决办法2:使用Lock和Condition实现线程间通信

一、两个线程synchronized写法-结果无问题

package com.atguigu.signcenter.thread;/*** 线程之间的通信-两个线程synchronized写法* @author: jd* @create: 2024-09-02*/
public class ThreadWaitNotifyDemo {public static void main(String[] args) {AirConditioner airConditioner = new AirConditioner();new Thread(() -> {for (int i = 0; i < 10; i++) {try {airConditioner.increment();} catch (InterruptedException e) {e.printStackTrace();}}}, "A").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {airConditioner.decrement();} catch (InterruptedException e) {e.printStackTrace();}}}, "B").start();}
}class AirConditioner {  // 资源类private int number = 0;public synchronized void increment() throws InterruptedException {// 1. 判断if (number != 0) {this.wait();}// 2. 干活number++;System.out.println(Thread.currentThread().getName() + "\t" + number);// 3. 通知this.notifyAll();}public synchronized void decrement() throws InterruptedException {// 1. 判断if (number == 0) {this.wait();}// 2. 干活number--;System.out.println(Thread.currentThread().getName() + "\t" + number);// 3. 通知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

二(一)、四个线程synchronized写法-问题及解决办法

换成4个线程会导致错误,虚假唤醒。
原因:在java多线程判断时,不能用if,程序出事出在了判断上面,突然有一添加的线程进到if了,突然中断了交出控制权,没有进行验证,而是直接走下去了,加了两次,甚至多次。
在这里插入图片描述

中断和虚假唤醒是可能产生的,所以要用loop循环,if只判断一次,while是只要唤醒就要拉回来再判断一次。if换成while。(也就是说在四个线程下,有可能两个increment线程都在if中wait,当其被唤醒时,不会再次判断number是否满足条件,而直接执行number++,因此会导致number大于1的情况,同理也会出现number小于0的情况)

如果使用if判断,则会导致虚假唤醒:代码及现象

package com.atguigu.signcenter.thread;/*** 线程之间的通信-两个、四个线程synchronized写法* @author: jd* @create: 2024-09-02*/
public class ThreadWaitNotifyDemo {public static void main(String[] args) {AirConditioner airConditioner = new AirConditioner();new Thread(() -> {for (int i = 0; i < 10; i++) {try {airConditioner.increment();} catch (InterruptedException e) {e.printStackTrace();}}}, "A").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {airConditioner.decrement();} catch (InterruptedException e) {e.printStackTrace();}}}, "B").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {airConditioner.increment();} catch (InterruptedException e) {e.printStackTrace();}}}, "C").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {airConditioner.decrement();} catch (InterruptedException e) {e.printStackTrace();}}}, "D").start();}
}class AirConditioner {  // 资源类private int number = 0;public synchronized void increment() throws InterruptedException {// 1. 判断if (number != 0) {this.wait();}// 2. 干活number++;System.out.println(Thread.currentThread().getName() + "\t" + number);// 3. 通知this.notifyAll();}public synchronized void decrement() throws InterruptedException {// 1. 判断if (number == 0) {this.wait();}// 2. 干活number--;System.out.println(Thread.currentThread().getName() + "\t" + number);// 3. 通知this.notifyAll();}
}

现象

A	1
B	0
A	1
B	0
C	1
B	0
A	1
B	0
C	1
D	0
B	-1
B	-2
B	-3
B	-4
B	-5
B	-6
A	-5
D	-6
D	-7
D	-8
D	-9
D	-10
D	-11
D	-12
D	-13
D	-14
C	-13
A	-12
C	-11
A	-10
C	-9
A	-8
C	-7
A	-6
C	-5
A	-4
C	-3
A	-2
C	-1

图示为什么会出现问题
在使用if判断两个线程的情况下,阻塞的线程只有两种情况,此时不会出现任何问题;
而使用if在四个线程的情况下,可能存在这种情况:

  1. 最开始+线程进行了增加操作NotifyAll;
  2. 此时+'线程抢占到执行权,进入if判断进入阻塞状态;
  3. +线程又抢到了执行权,同样进入if判断阻塞;
  4. -线程抢占执行权进行减操作,NotifyAll;
  5. +'线程抢占执行权,进行增加操作,NotifyAll;
  6. +线程抢占执行权,进行增加操作 (此时便出现了number=2的情况)
    使用while就不会出现这种问题,因为在NotifyAll线程激活运行后,会进行二次判断!
    在这里插入图片描述

二(二)、4线程问题 解决办法1:使用while进行条件判断

解决此问题:
使用while进行条件判断

  1. 高内聚第耦合的前提下,线程操作资源类
  2. 判断/干活/通知
  3. 多线程交互中,必须要防止多线程的虚假唤醒,也即(在多线程的判断中不许用if只能用while)
    解决代码:
package com.atguigu.signcenter.thread;/*** 线程之间的通信-两个、四个线程synchronized写法* @author: jd* @create: 2024-09-02*/
public class ThreadWaitNotifyDemo {public static void main(String[] args) {AirConditioner airConditioner = new AirConditioner();new Thread(() -> {for (int i = 0; i < 10; i++) {try {airConditioner.increment();} catch (InterruptedException e) {e.printStackTrace();}}}, "A").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {airConditioner.decrement();} catch (InterruptedException e) {e.printStackTrace();}}}, "B").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {airConditioner.increment();} catch (InterruptedException e) {e.printStackTrace();}}}, "C").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {airConditioner.decrement();} catch (InterruptedException e) {e.printStackTrace();}}}, "D").start();}
}
/*
class AirConditioner {  // 资源类private int number = 0;public synchronized void increment() throws InterruptedException {// 1. 判断if (number != 0) {this.wait();}// 2. 干活number++;System.out.println(Thread.currentThread().getName() + "\t" + number);// 3. 通知this.notifyAll();}public synchronized void decrement() throws InterruptedException {// 1. 判断if (number == 0) {this.wait();}// 2. 干活number--;System.out.println(Thread.currentThread().getName() + "\t" + number);// 3. 通知this.notifyAll();}*/class AirConditioner {  // 资源类private int number = 0;public synchronized void increment() throws InterruptedException {// 1. 判断while (number != 0) {this.wait();}// 2. 干活number++;System.out.println(Thread.currentThread().getName() + "\t" + number);// 3. 通知this.notifyAll();}public synchronized void decrement() throws InterruptedException {// 1. 判断while (number == 0) {this.wait();}// 2. 干活number--;System.out.println(Thread.currentThread().getName() + "\t" + number);// 3. 通知this.notifyAll();}
}

正常结果:

A	1
B	0
A	1
B	0
A	1
B	0
C	1
B	0
A	1
B	0
C	1
B	0
A	1
D	0
C	1
B	0
A	1
D	0
C	1
B	0
A	1
D	0
C	1
B	0
A	1
D	0
C	1
B	0
A	1
D	0
C	1
D	0
A	1
D	0
C	1
D	0
C	1
D	0
C	1
D	0

二(三)、4线程问题 解决办法2:使用Lock和Condition实现线程间通信

通过Java8的Lock和Condition接口(await、signal、signalAll),可以替换synchronized与Object monitor方法(wait、notify、notifyAll)
在这里插入图片描述
这里我们还是使用3.2中的例子,4个线程,两个打印1两个打印0,让其交替打印,分别打印十次

package com.atguigu.signcenter.thread;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*** @author: jd* @create: 2024-09-02*/
public class ThreadWaitNotifyDemo2 {public static void main(String[] args) {AirConditioner2 airConditioner = new AirConditioner2();new Thread(() -> {for (int i = 0; i < 10; i++) {try {airConditioner.increment();} catch (InterruptedException e) {e.printStackTrace();}}}, "A").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {airConditioner.decrement();} catch (InterruptedException e) {e.printStackTrace();}}}, "B").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {airConditioner.increment();} catch (InterruptedException e) {e.printStackTrace();}}}, "C").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {airConditioner.decrement();} catch (InterruptedException e) {e.printStackTrace();}}}, "D").start();}
}class AirConditioner2 {  // 资源类private int number = 0;// 使用java8 lock 和 condition接口实现private Lock lock = new ReentrantLock();private Condition condition = lock.newCondition();public void increment() throws InterruptedException {lock.lock();try {// 1. 判断while (number != 0) {condition.await(); // this.wait();}// 2. 干活number++;System.out.println(Thread.currentThread().getName() + "\t" + number);// 3. 通知condition.signalAll(); // this.notifyAll();}catch (Exception e) {}finally {lock.unlock();}}public void decrement() throws InterruptedException {lock.lock();try {// 1. 判断while (number == 0) {condition.await(); // this.wait();}// 2. 干活number--;System.out.println(Thread.currentThread().getName() + "\t" + number);// 3. 通知condition.signalAll(); // this.notifyAll();}catch (Exception e) {}finally {lock.unlock();}}
}

码字不易,请大家多多指教~

这篇关于【多线程】线程间通信 之虚假唤醒和中断的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1130819

相关文章

JDK21对虚拟线程的几种用法实践指南

《JDK21对虚拟线程的几种用法实践指南》虚拟线程是Java中的一种轻量级线程,由JVM管理,特别适合于I/O密集型任务,:本文主要介绍JDK21对虚拟线程的几种用法,文中通过代码介绍的非常详细,... 目录一、参考官方文档二、什么是虚拟线程三、几种用法1、Thread.ofVirtual().start(

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

Java 线程池+分布式实现代码

《Java线程池+分布式实现代码》在Java开发中,池通过预先创建并管理一定数量的资源,避免频繁创建和销毁资源带来的性能开销,从而提高系统效率,:本文主要介绍Java线程池+分布式实现代码,需要... 目录1. 线程池1.1 自定义线程池实现1.1.1 线程池核心1.1.2 代码示例1.2 总结流程2. J

Java JUC并发集合详解之线程安全容器完全攻略

《JavaJUC并发集合详解之线程安全容器完全攻略》Java通过java.util.concurrent(JUC)包提供了一整套线程安全的并发容器,它们不仅是简单的同步包装,更是基于精妙并发算法构建... 目录一、为什么需要JUC并发集合?二、核心并发集合分类与详解三、选型指南:如何选择合适的并发容器?在多

C#使用SendMessage实现进程间通信的示例代码

《C#使用SendMessage实现进程间通信的示例代码》在软件开发中,进程间通信(IPC)是关键技术之一,C#通过调用WindowsAPI的SendMessage函数实现这一功能,本文将通过实例介绍... 目录第一章:SendMessage的底层原理揭秘第二章:构建跨进程通信桥梁2.1 定义通信协议2.2

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

Java中如何正确的停掉线程

《Java中如何正确的停掉线程》Java通过interrupt()通知线程停止而非强制,确保线程自主处理中断,避免数据损坏,线程池的shutdown()等待任务完成,shutdownNow()强制中断... 目录为什么不强制停止为什么 Java 不提供强制停止线程的能力呢?如何用interrupt停止线程s

python 线程池顺序执行的方法实现

《python线程池顺序执行的方法实现》在Python中,线程池默认是并发执行任务的,但若需要实现任务的顺序执行,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋... 目录方案一:强制单线程(伪顺序执行)方案二:按提交顺序获取结果方案三:任务间依赖控制方案四:队列顺序消

Python多线程实现大文件快速下载的代码实现

《Python多线程实现大文件快速下载的代码实现》在互联网时代,文件下载是日常操作之一,尤其是大文件,然而,网络条件不稳定或带宽有限时,下载速度会变得很慢,本文将介绍如何使用Python实现多线程下载... 目录引言一、多线程下载原理二、python实现多线程下载代码说明:三、实战案例四、注意事项五、总结引

Python多线程应用中的卡死问题优化方案指南

《Python多线程应用中的卡死问题优化方案指南》在利用Python语言开发某查询软件时,遇到了点击搜索按钮后软件卡死的问题,本文将简单分析一下出现的原因以及对应的优化方案,希望对大家有所帮助... 目录问题描述优化方案1. 网络请求优化2. 多线程架构优化3. 全局异常处理4. 配置管理优化优化效果1.