JavaEE 第7节 线程饥饿及其解决办法

目录

一、什么是线程饥饿?

二、线程饥饿的解决办法

*wait()与notify()方法解决线程饥饿

1、wait(等待)

2、notify(通知)

1)notify

2)notifyAll

3)关于wait方法的一些补充

1、wait的方法的三个功能是原子性的:

2、sleep与wait方法的异同

相同点:

不同点:

        所属类:

        锁释放:

        使用场景:

        调用位置:


一、什么是线程饥饿?

线程饥饿Thread Starvation)指的是多线程中,某些线程无法获得相关资源或者执行机会(阻塞,BLOCKED)长时间如此对线程的推进和响应造成明显影响的现象

左边的蘑菇头和右边的小人都是线程,左边的蘑菇头因为在所中没有自己想要的资源所以从锁中出来,但是由于它的优先级比较高一从锁里出来,转头有跑进锁里面了,完全没有给右边的小进去的机会

当饥饿到一定程度,赋予线程的任务即使完成也不在具有实际意义的时候就说明这个线程被饿死了

二、线程饥饿的解决办法

线程饥饿原因:

线程产生饥饿,主要是因为系统对线程调度方式不够公平或者不合理导致的

想要解决饥饿问题,我们只需要通过一些手段,对线程的调度进行合理的干预即可

比如上图中的蘑菇头线程,如果自己目前拿不到想要的资源结果,那就先把锁让给别的线程,不要自己在锁这里反复横跳!等别的线程把资源变量改成蘑菇头想要的结果的时候,然后再让蘑菇头进来拿锁。

*wait()与notify()方法解决线程饥饿

这两个方法可以让多个线程按照某一个逻辑上的先后顺序执行,从而避免线程的饿死。

注意两个方法都必须使用锁对象调用!

1、wait(等待)

wait()方法有三个功能:

1)让当前线程释放持有的锁

2)让当前线程进入WAITING状态(或者TIMED_WAITING,取决于括号中是否含有时间参数,ms)。

3)检测其他线程是否调用了同对象的notify()方法,如果调用了notify()方法,那么唤醒正在睡眠的线程


注释:

这三个功能都已经集成到的wait方法上,且是原子性的!(最后一个小节讲解原因)

使用方式:

public class Threads {private static final Object lock = new Object();public static void main(String[] args) throws InterruptedException {Thread thread1=new Thread(()->{synchronized (lock){try {lock.wait();//进入等待状态(WAITING),必须用lock调用} catch (InterruptedException e) {//与sleep方法类似,需要try catch,// 但是wait不能自动唤醒自己,只能考notify方法e.printStackTrace();}}});thread1.start();thread1.join();}
}

上面这个程序会造成程序的卡死。

原因:

wait()没有时间参数,那么就必须等其他线程用notify()方法唤醒它,但是我没有写notify方法,所以thread1会一直处在WAITINGZ状态,但是主线程有用了join()方法,所以主线程一直在等thread1线程运行完毕,进而造成程序的卡死,注意这不是死锁!

如果不想死等,那么可以使用带时间参数的重载方法:

public class Threads {private static final Object lock = new Object();public static void main(String[] args) throws InterruptedException {Thread thread1=new Thread(()->{synchronized (lock){try {lock.wait(1000);//带参数,1秒} catch (InterruptedException e) {//与sleep方法类似,需要try catche.printStackTrace();}}});thread1.start();thread1.join();}
}

2、notify(通知)

1)notify

我们刚才看到了,仅仅用wait方法是不够的,需要与notify方法搭配。

notify方法可以把因为wait而睡眠的线程唤醒:

public class Threads {private static final Object lock = new Object();public static void main(String[] args) throws InterruptedException {Thread thread1=new Thread(()->{synchronized (lock){try {System.out.println("thread1即将进入睡眠,等待其他线程用notify方法唤醒");lock.wait();System.out.println("thread1成功被唤醒");} catch (InterruptedException e) {e.printStackTrace();}}});Thread thread2=new Thread(()->{synchronized (lock){try {Thread.sleep(1000);//确保thread1线程先执行,不然唤醒个寂寞} catch (InterruptedException e) {e.printStackTrace();}System.out.println("thread2即将使用norify方法唤醒thread1");lock.notify();//同样,必须用lock调用!!System.out.println("thread2已唤醒thread1");}});thread1.start();thread2.start();thread1.join();thread2.join();}
}

执行结果:

倘若同时有多个线程调用wait方法,那么notify方法会"随机"的唤醒一个线程(具体由操作系统决定):

public class Threads {private static final Object lock = new Object();public static void main(String[] args) throws InterruptedException {Thread thread1 = new Thread(() -> {synchronized (lock) {try {System.out.println("thread1即将进入睡眠,等待其他线程用notify方法唤醒");lock.wait();System.out.println("thread1成功被唤醒");} catch (InterruptedException e) {e.printStackTrace();}}});Thread thread2 = new Thread(() -> {synchronized (lock) {try {System.out.println("thread2即将进入睡眠,等待其他线程用notify方法唤醒");lock.wait();System.out.println("thread2成功被唤醒");} catch (InterruptedException e) {e.printStackTrace();}}});Thread thread3 = new Thread(() -> {synchronized (lock) {try {System.out.println("thread3即将进入睡眠,等待其他线程用notify方法唤醒");lock.wait();System.out.println("thread3成功被唤醒");} catch (InterruptedException e) {e.printStackTrace();}}});Thread thread4 = new Thread(() -> {synchronized (lock) {try {Thread.sleep(1000);//确保thread1线程先执行,不然唤醒个寂寞} catch (InterruptedException e) {e.printStackTrace();}System.out.println("thread4即将使用norify方法唤醒thread1");lock.notify();System.out.println("thread2已唤醒thread1");}});thread1.start();thread2.start();thread3.start();thread4.start();thread1.join();thread2.join();thread3.join();thread4.join();}
}

执行结果只会唤醒一个线程:

注意:

多次notify不会有副作用,即使没有线程wait过:

public class Threads {private static final Object lock = new Object();public static void main(String[] args) throws InterruptedException {Thread t1=new Thread(()->{synchronized (lock){lock.notify();lock.notify();lock.notify();lock.notify();lock.notify();lock.notify();lock.notify();lock.notify();lock.notify();}});t1.start();t1.join();}
}

当然不建议这样做,谁会这么无聊🤪

2)notifyAll

如果想要全部唤醒,也有办法,那就是使用notifyAll方法:

public class Threads {private static final Object lock = new Object();public static void main(String[] args) throws InterruptedException {Thread thread1 = new Thread(() -> {synchronized (lock) {try {System.out.println("thread1即将进入睡眠,等待其他线程用notify方法唤醒");lock.wait();System.out.println("thread1成功被唤醒");} catch (InterruptedException e) {e.printStackTrace();}}});Thread thread2 = new Thread(() -> {synchronized (lock) {try {System.out.println("thread2即将进入睡眠,等待其他线程用notify方法唤醒");lock.wait();System.out.println("thread2成功被唤醒");} catch (InterruptedException e) {e.printStackTrace();}}});Thread thread3 = new Thread(() -> {synchronized (lock) {try {System.out.println("thread3即将进入睡眠,等待其他线程用notify方法唤醒");lock.wait();System.out.println("thread3成功被唤醒");} catch (InterruptedException e) {e.printStackTrace();}}});Thread thread4 = new Thread(() -> {try {Thread.sleep(1000);//确保其他线程先执行,不然唤醒个寂寞} catch (InterruptedException e) {e.printStackTrace();}synchronized (lock){System.out.println("即将唤醒所有线程");lock.notifyAll();/**换成notifyAll方法,其他代码都没有变*/System.out.println("thread2已唤醒所有线程");}});thread1.start();thread2.start();thread3.start();thread4.start();thread1.join();thread2.join();thread3.join();thread4.join();}
}

其中一个执行结果:

注意:

1)由于thread1、thread2、thread3是同时被唤醒的。之后这3个线程会进入锁竞争只有一个线程可以拿到锁然后执行,因此thread1、thread2、thread3三个线程那个先执行完是不确定的。

2)notify方法不像wait方法,wait方法调用的时候会释放当前对象的锁,但是notify方法没有这样的功能!!!也就是说,只有调用notify的线程释放了锁,被notify唤醒的线程才有机会执行。(notifyAll与notify是一样的)


3)关于wait方法的一些补充

1、wait的方法的三个功能是原子性的:

1)让当前线程释放持有的锁

2)让当前线程进入WAITING状态(或者TIMED_WAITING,取决于括号中是否含有时间参数,ms)。

3)检测其他线程是否调用了同对象的notify()方法,如果调用了notify()方法,那么唤醒正在睡眠的线程。(InterruptedException也会提前唤醒wait过的线程

假设不满足原子性:

有t1和t2两个线程,t1线程调用wait方法,要等待t2线程用notify唤醒t1。

若t1在执行完第一个功能后,由于线程调度的原因,t1在没有进入睡眠的状态就提前释放了锁,给t2,那么会出现这个情况:

t2线程提前执行了notify方法,t2线程运行完后,锁就交给了t1,紧接着t1就开始进入睡眠状态,等待t2线程用notify唤醒t1?这显然不符合逻辑。


2、sleep与wait方法的异同

相同点:

1)都可以暂停线程执行

2)都会抛出InterruptedException,需要对异常进行处理

注意

调用了wait方法的线程如果捕获到InterruptedException,此线程就会终止

不同点:
        所属类:

                1)sleep是Thread类的静态方法。

                2)wait是Object类的示例方法,且必须由锁对象调用。

        锁释放:

                1)用睡眠不会释放锁,也就是抱着锁睡:

                2)wait会释放锁,然后再睡。

        使用场景:

                1)sleep用于暂停线程执行一段时间,用的比较广泛。

                2)wait一般用在线程之间的通信,等待某个条件成立,接收notify信号。

        调用位置:

                1)sleep可以在任意地方调用。

                2)wait必须在同步块中调用(synchronized)。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【mysql 第二篇章】请求到真正执行 SQL 到底是一个怎么样的过程?

从用户调用到SQL执行的流程中间发生了什么事情 1、网络请求使用 线程 来处理,当数据库连接池中监听到有连接请求,这个时候会分配一个线程来处理。 2、SQL接口 负责接收 SQL 语句,当线程监听到有请求和读取数据的之后,将 SQL 语句…

鸿蒙(API 12 Beta3版)【视频解码】 音视频编码

开发者可以调用本模块的Native API接口,完成视频解码,即将媒体数据解码成YUV文件或送显。 当前支持的解码能力如下: 视频硬解类型视频软解类型AVC(H.264)、HEVC(H.265)AVC(H.264) 视频解码软/硬件解码存在差异,基于MimeType创建…

【实现100个unity特效之15】最简单的方法使用shader graphs实现2d非像素和像素树叶草的随风摇摆效果

文章目录 前言非像素树叶草飘动效果新建材质效果像素树叶草飘动效果参考完结 前言 本文只是实现一个简单版本的2d树叶草随风摇摆的效果,如果你想要实现更加复杂的效果,包括2d互动草,你可以参考我之前的文章: 【推荐100个unity插件…

Docker 入门全攻略:安装、操作与常用命令指南

目录 Docker 入门全攻略:安装、操作与常用命令指南 一、引言 二、Docker 下载与安装 2.1 Docker 的系统要求 2.2 安装步骤 ①对于 Windows 的安装指南 ②对于 macOS 的安装指南 ③对于 Linux 的安装指南 三、Docker 的基本概念 3.1 镜像(Image…

java消息队列ActiveMQ

安装 前置条件 activemq的运行依赖于jdk,需要提前安装jdk如果已经安装了jdk,需要根据jdk的版本来选择对应的版本进行安装activemq版本对应在官网上,使用java -version 看jdk的版本注意:jdk和mq的版本不一致会报错,电脑…

MySQL笔记-基础篇(一):查询

博客主页: 南来_北往 系列专栏:Spring Boot实战 MySQL是一种广泛使用的关系型数据库管理系统,它基于结构化查询语言(SQL)来管理和操作数据。下面将依次探讨MySQL查询的各个方面,确保理解扎实,能够在实…

计算机视觉——凸包计算

现在有一大堆点,然后你要找出一个可以围住这些点且面积最小的凸多边形,这个凸多边形称为凸包。 显而易见,如果要面积最小,那凸包的顶点势必得是这一大堆点的几个点,你也可以想成是用一条橡皮筋把这些点圈起来。 先把各…

Python中的 `break` 语句:掌握循环控制的艺术

Python中的 break 语句:掌握循环控制的艺术 下滑即可查看博客内容 🌈 欢迎莅临我的个人主页 👈这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地!🎇 🎓 博主简介:985高校的普通本硕…

【大模型系列篇】论文解读:Transformer - Attention Is All You Need (2017)

Attention Is All You Need (Transformer) 是当今大模型初学者必读的一篇论文,已经有不少业内大佬都翻译解读过这篇论文,此处仅作为自己学习的记录。该论文是由谷歌机器翻译团队于2017年发表在NIPS ,提出了一个只基于attention的结构来处理序…

【iOS】OC关键字总结及底层原理(上)

目录 线程安全相关的关键字atomic&nonatomic 作用域相关的关键字static、extern、const&auto 读写权限相关和指定方法名的关键字内存管理相关的关键字(或方法)1. 引用计数的存储SideTableretain方法源码分析release方法源码分析dealloc方法源码分…

嵌入式初学-C语言-十九

指针的引入 为函数修改实参提供支持为动态内存管理提供支持为动态数据及结构提供支持为内存访问提供另一种途径 指针的概述 内存地址: 系统为了内存管理的方便将内存划分为一个个内存单元(一个内存单元占一个字节),并为每一个…

用Vue和Axios将数据库数据显示在前端页面

在本次实例中Vue只用在了前端部分&#xff0c;Axios用于向后端请求数据&#xff0c;我们这里要用到Ajax技术来访问后端数据。 HTML&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name&quo…

全新博客X主题/简约WordPress主题模板/主题巴巴/免授权版源码+自适应设计

源码简介&#xff1a; 博客X这款超酷的Wordpress主题&#xff0c;是主题巴巴团队打造的设计杰作。想象一下&#xff0c;你的博客首页能展示那些炫酷的幻灯片置顶文章、还有各种精心策划的专题列表&#xff0c;这些内容模块的设计简直吸睛了&#xff0c;能让来访的用户眼前一亮…

数据结构和算法|递归算法那些事(递归算法的时间复杂度、尾递归优化、斐波那契数列)

对于文章的第一部分&#xff0c;递归算法的时间复杂度&#xff0c;来自于代码随想录文章:通过一道面试题目&#xff0c;讲一讲递归算法的时间复杂度&#xff01; 对于第二节尾递归优化来自于B站&#xff1a;尾递归优化&#xff1a;你的递归调用是如何被优化的&#xff1f; 文章…

XML(可扩展标记语言)

QDomDocument doc;QDomElement ss doc.createElement("root");//创建标签 //ss标签添加到文档对象doc.appendChild(ss);//doc.save()auto hero doc.createElement("hero");ss.appendChild(hero);hero.setAttribute("id",10086);//为hero添加属…

MySQL——数据表的基本操作(一)创建数据表

数据库创建成功后,就需要创建数据表。所谓创建数据表指的是在已存在的数据库中建立新表。需要注意的是&#xff0c;在操作数据表之前&#xff0c;应该使用 “ USE 数据库名 ” 指定操作是在哪个数据库中进行&#xff0c;否则会抛出 “ No database selected ” 错误。创建数据表…

Tomcat 使用和配置文件(详解)

一.tomcat 介绍 1. tomcat 概述 自从JSP发布之后&#xff0c;推出了各式各样的JSP引擎。Apache Group在完成GNUJSP1.0的开发以后&#xff0c;开始考虑在SUN的JSWDK基础上开发一个可以直接提供Web服务的JSP服务器&#xff0c;当然同时也支持 Servlet&#xff0c;这样Tomcat就诞…

[自学记录09*]关于模糊效果降采样优化性能的小实验

一、降采样在模糊中的优化 这两天接手了几个高度定制化的模糊&#xff0c;包括不限于放射和旋转状的径向模糊&#xff0c;移轴模糊&#xff0c;景深的散景模糊等等&#xff0c;这些效果在游戏中非常常见。 其实模糊的原理都差不多&#xff0c;无非就是对UV偏移后重新采样再求…

《Python爬虫逆向实战》绕过debugger的方法汇总

禁用断点 打开控制台&#xff0c;点击右边的禁用断点按钮。 点击之后再刷新下&#xff0c;就会发现debugger失效了。 注&#xff1a;这种方法有个 弊端&#xff0c;就是我们在代码中下的断点也都将失效。 Add script to ignore list 在代码文件中任意位置右键&#xff0c;然…

51单片机—串口

一、 串口基本认知 串行接口简称串口&#xff0c;也称串行通信接口或串行通讯接口&#xff08;通常指COM接口&#xff09;&#xff0c;是采用串行通信方 式的扩展接口。串行接口&#xff08;Serial Interface&#xff09;是指数据一位一位地顺序传送。其特点是通信线路简 单&a…