多线程(初阶五:wait和notify)

目录

一、概念

二、用法

(1)举个栗子:

(2)wait和notify的使用

1、没有上锁的wait

2、当一个线程被wait,但没有其他线程notify来释放这个wait

3、两个线程,有一个线程wait,有一个线程notify来释放wait

4、notifyAll

(3)wait的三个选项

三、wait、sleep、join


一、概念

我们知道,多线程在系统中的调度是随机的,我们不能干预多个线程的执行顺序,但是我们可以使某个线程放弃被系统调用,让其他线程先被调用,这样,可以达到我们的预期效果;

wait就是让多线程进行锁竞争的时候,让后执行的线程,放弃和别的线程进行锁竞争,别的线程执行完后,别的线程使用notify,将wait的线程不想进行锁竞争这个信息释放掉,再次和其他线程锁竞争。等待,通知的机制(和join用途类似)


二、用法

(1)举个栗子:

现在有很多滑稽老铁要去ATM里,滑稽A是取钱的,滑稽B是存钱的,滑稽C是运钞票的人员,负责给ATM机补充钱,防止ATM机没钱了,别人取不到钱。而这里的滑稽A,滑稽B,滑稽C我们当做是线程,每次去ATM机里,只能有一个人进去,相当于上锁了,其他人不能进去,等ATM机里面的人完成操作后,出来后,别的人才能进去,但是这里是多线程的原因,其他线程会有锁竞争。     

当A进去ATM机里后,就上锁,其他人不能进去,如图:

把滑稽A比作是线程,当A线程进去后就会上锁,A线程要进行取钱的操作,其他线程不能进去操作,当A线程执行完自己的操作后,其他线程才能去锁竞争。

但是如果ATM机里面没有钱时,A线程就不能完成取钱这个操作,它会退出ATM机,但退出后呢,它因为没有完成取钱的操作,就会想继续进去ATM里面,完成取钱这个操作,就会继续和其他线程进行锁竞争,因为A线程拿到了锁,处于RUNNABLE状态,其他线程因为阻塞,处于BLOCKED状态,需要被系统唤醒后,才能去竞争锁,但是线程A呢,不用唤醒就能去竞争锁,后面又被A线程拿到锁的可能性还是很大的(类似近水楼台先得月)。如果这样子,那线程A频繁的进去又出来,干不了事,但是其他线程也不能进去操作,用通俗的话说,就是占着茅坑不拉屎的意思。也就出现线程安全问题了。其他线程,无法拿到锁,这个情况称为 “线程饿死”。

这里的线程A的代码大概逻辑是这样的:

当A线程没有取到钱,就会一直重复加锁,解锁的操作。

这样的bug没有死锁那么严重,但也是要解决的。那如何解决呢。这时,就可以用wait和notify了。期望改进成,如下图:

这里的wait内部做了三件事

(1)释放锁,给其他线程竞争锁

(2)进入阻塞等待

(3)等其他线程使用notify后,解除wait,参与到锁竞争中

(2)wait和notify的使用

wait的使用前提必须是当前对象被上锁了才能使用,不能你对象没被上锁,就wait了,那也不知道是在wait谁。同时,有线程wait了,也必须有其他线程notify来释放这个wait,不然这个wait就会一直阻塞。

1、没有上锁的wait

代码:

public class TestDemo1 {
    public static void main(String[] args) throws InterruptedException {
        Object locker = new Object();
        locker.wait();
    }
}

执行结果:

2、当一个线程被wait,但没有其他线程notify来释放这个wait

代码:

public class TestDemo3 {
    public static void main(String[] args) {
        Object locker = new Object();
        Thread t1 = new Thread(() -> {
            synchronized (locker) {
                System.out.println("wait之前");
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("wait之后");
            }
        });
        t1.start();
    }
}

执行结果:

打印不了 “wait之后”,一直是阻塞等待状态,在jconsole中,状态如图

3、两个线程,有一个线程wait,有一个线程notify来释放wait

代码:

public class TestDemo2 {
    public static void main(String[] args) {
        Object locker = new Object();
        Thread t1 = new Thread(() -> {
            synchronized (locker) {
                System.out.println("t1 wait之前");
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("t2 wait之后");
            }
        });
        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            synchronized (locker) {
                System.out.println("t2 notify之前");
                locker.notify();
                System.out.println("t2 notify之后");
            }
        });
        t1.start();
        t2.start();
    }
}

注意:这里的释放wait的notify,用的对象必须是要一样的,如果不一样,wait是不能被释放的,t1也就不能被唤醒了.

在系统中,notify可以不用上锁,但是在java中,规定了要上锁,而且上锁的对象也要和notify对象一样,所以和系统是有区别的。

执行结果:

结果解析:

t1 和 t2 执行的时候

(1)因为t1 sleep了1秒,所以能保证t1 先wait,所以先打印 “t1 wait之前”,这时,t1就进入阻塞等待状态。

(2)t2线程sleep了1秒后,获得这个locker锁,打印“t2 notify 之前”,当t2线程执行了notify后,t1 线程的wait就被释放了。

(3)因为t2还在持有锁,所以t1会还会进入阻塞,t2打印 “t2 notify之后” ,释放锁。

(4)t1拿到锁,再打印“t1 wait之后”。

4、notifyAll

唤醒等待这个对象的所有线程;假设有很多个线程,都使用同一个对象wait,这时,使用notifyAll,所有使用了这个对象的wait的线程,都会被唤醒。

注意:当这些线程都被唤醒时,就要重新获取锁,他们还是要进行锁竞争的,这里也就相当于串行执行了(线程调度还是随机调度的)。而且使用notifyAll后,全部使用同一对象wait的线程,都被唤醒了,不好控制,更加推荐使用notify。

(3)wait的三个选项

如图:

没有参数的就是死等,但是很多情况,死等是不合理的,所以我们加参数,就是让某个线程在一定时间wait,如果超出了这个时间,就不wait了,直接过掉wait。

有一个参数的精确范围是毫秒级别,两个参数的精确范围是纳秒级别。


三、wait、sleep、join

wait:需要搭配synchronized使用,线程wait时,处于WAITING状态,需要其他线程notify后,才能被唤醒,或者设置时间,到时就唤醒,可以兜底。

sleep:线程sleep时,要到一定休眠时间才能被唤醒,但是也能被interrupt终止,但是这种情况是会抛异常的,是非常规手段,不符合我们预期的效果。

join:啥线程调用join,当前线程就要等啥线程执行完,才能之前当前线程;和wait一样有参数可以选择,到时就不等了。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

目标检测——Mask R-CNN算法解读

论文:Mask R-CNN 作者:Kaiming He Georgia Gkioxari Piotr Dollar Ross Girshick 链接:https://arxiv.org/abs/1703.06870 代码:https://github.com/facebookresearch/Detectron R-CNN系列其他文章: R-CNN算法解读SPP…

Leecode 【一】

环形链表: 给你一个链表的头节点 head ,判断链表中是否有环。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&…

教你用AI做治愈系风景动态视频

这几天刚发布AI小红薯商单变现案例库,同学们私信表示案例库启发很大,很有价值,只是能不能再多来点手把手式的实操教程! 这是个好需求,没问题~,今天就手把手地给大家分享一个近半年来,在各大平台…

优思学院:六西格玛项目中什么是顾客之声?

让客户的声音成就您的成功! 顾客之声(Voice of customer-VOC)是六西格玛项目中的一个重要概念,指的是从顾客的角度和需求出发,通过收集和分析顾客的反馈和意见,以了解他们对产品或服务的期望、满意度和不满意之处。顾客之声的目的…

分享几个可以免费使用GPT工具

1. 国产可以使用GPT3.5和4.0的网站,每日有免费的使用额度,响应速度,注册时不用使用手机号,等个人信息,注重用户隐私,好评! 一个好用的ChatGPT系统 ,可以免费使用3.5 和 4.0https://…

springboot+java校园自助洗衣机预约系统的分析与设计ssm+jsp

洗衣服是每个人都必须做的事情,而洗衣机更成为了人们常见的电器,但是单个洗衣机价格不菲,如果每人都买,就会造成资源的冗余。所有就出现了公用设备,随着时代的发展,很多公用都开始向着无人看守的自助模式经…

ChatGLM2详细安装部署(chatglm2大模型安装步骤三)

ChatGLM2安装部署 1.服务器配置 服务器系统:Centos7.9 x64 显卡:RTX3090 (24G) 虚拟环境:Miniconda3 2.安装部署 2.1 ChatGLM2下载 输入命令:git clone https://github.moeyy.xyz/https://github.com/THUDM/ChatGLM2-6B.git 输入命令:cd ChatGLM2-6B 注:https://g…

【note: This is an issue with the package mentioned above, not pip.】

安装gym时出现问题,note: This is an issue with the package mentioned above, not pip. 报错原因: 缺失了某些依赖模块,所以安装报错。 Collecting package metadata (current_repodata.json): done Solving environment: failed with in…

2021年11月10日 Go生态洞察:Twelve Years of Go

🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…

关于电脑提示vcruntime140_1.dll无法继续执行代码的解决办法

vcruntime140_1.dll是Visual C运行时库的一个组成部分,它包含了大量用于支持C应用程序运行时的功能。这个文件通常在开发和使用C程序时被调用,特别是在使用Microsoft Visual Studio进行开发时。vcruntime140_1.dll文件丢失或损坏会导致C程序无法正常运行…

python 使用reportlab打造29页图文并茂pdf(全网reportlab最强pdf自动化生成代码)

python 使用reportlab打造29页图文并茂pdf(全网reportlab最强pdf自动化生成代码) 这次项目所使用的代码如果同志们可以灵活使用,基本上可以解决百分之九十以上的pdf模板自动化生成。 最近博主,做了一个项目,使用reportlab制作pd…

JDK10新特性

目录 传送门一、局部变量类型推断1、概念1.1、简单局部变量推断1.2、自定义对象的推断 2、可以使用的场景3、不能使用的场景4、注意事项 二、垃圾回收器的优化1、前置知识1.1、10种垃圾回收器1.2、分代与分区1.3、10种垃圾回收器小总结 2、优化点 三、新增API1、集合新方法copy…

多线程(初阶六:单例模式)

一、单例模式的简单介绍 二、饿汉模式 三、懒汉模式 四、饿汉模式和懒汉模式的线程安全问题分析 一、单例模式的简单介绍 单例模式是一种设计模式,其中设计模式是软性的规定,与它关联的框架是硬性的规定,这些都是大佬已经设计好了的&…

【工作记录】spider-flow使用插件连接并操作mongodb数据库

前言 前面说过,spider-flow有着非常优秀的插件机制,可以通过插件实现功能的扩展。前面有小伙伴问到mongodb的集成使用,本文就来梳理下spider-flow中使用mongodb插件的过程,其实非常简单。 PS: spider-flow的作者已经实现了一些常…

线程池技术总结

1. 线程池解决了什么问题 线程池是集中管理线程的,以实现线程的重用,降低资源消耗,提高响应速度,提高线程的可管理性等。线程用于执行异步任务,单个的线程既是工作单元也是执行机制,从JDK1.5开始&#xff…

文心一言 VS 讯飞星火 VS chatgpt (147)-- 算法导论12.2 2题

二、用go语言,写出 TREE-MINIMUM 和 TREE-MAXIMUM 的递归版本。 灵捷3.5 。 文心一言: 以下是用 Go 语言编写的树的最大值和最小值的递归版本代码: package main import ( "fmt" ) type TreeNode struct { Val int Le…

哈希表 LeetCode 2661. 找出叠涂元素

2661. 找出叠涂元素 给你一个下标从 0 开始的整数数组 arr 和一个 m x n 的整数 矩阵 mat 。arr 和 mat 都包含范围 [1,m * n] 内的 所有 整数。 从下标 0 开始遍历 arr 中的每个下标 i ,并将包含整数 arr[i] 的 mat 单元格涂色。 请你找出 arr 中在 …

LeetCode105.从前序和中序遍历序列构造二叉树

这道题看完题想了几分钟就想到大概的思路了,但是在写的时候有很多细节没注意出了很多问题,然后写了1个多小时,其实这道题挺简单的。 首先,最基本的知识,先序遍历是根左右,中序遍历是左根右,那么…

重磅!GPT-4 API,全面开放使用!

7月7日,OpenAI在官网宣布,GPT-4 API全面开放使用。现所有付费API用户都可直接访问8K上下文的GPT-4,无需任何等待。 预计到7月底之前,OpenAI会向全新的开发人员开放GPT-4 API使用权限。(API详细使用说明地址&#xff1…

【AB平台数据建设】从实验平台到数据管道

文章目录 前言1.从AB实验平台聊起(1)AB平台在业务中的发挥那些作用(2)AB平台进行实验工作流介绍 2.实验平台底层数据管道最小MVP解构(1)数据管道数据从哪里来?(2)数据管道的输出数据有哪些? 小结 前言 AB实验平台是一种通过小范围放量,测试不…
最新文章