wait 和 notify

news2024/11/27 18:33:40

由于线程之间是抢占式执行的, 因此线程之间执行的先后顺序难以预知.但是实际开发中有时候我们希望合理的协调多个线程之间的执行先后顺序.
 

  • wait() / wait(long timeout): 让当前线程进入等待状态.
  • notify() / notifyAll(): 唤醒在当前对象上等待的线程.
     

 注意: wait, notify, notifyAll 都是 Object 类的方法.

1 wait()方法

wait 做的事情:

  • 使当前执行代码的线程进行等待. (把线程放到等待队列中)
  • 释放当前的锁
  • 满足一定条件时被唤醒, 重新尝试获取这个锁
     

wait 要搭配 synchronized 来使用. 脱离 synchronized 使用 wait 会直接抛出异常

wait 结束等待的条件:

  • 其他线程调用该对象的 notify 方法.
  • wait 等待时间超时 (wait 方法提供一个带有 timeout 参数的版本, 来指定等待时间).
  • 其他线程调用该等待线程的 interrupted 方法, 导致 wait 抛出 InterruptedException 异常.


代码示例: 观察wait()方法使用
 

package thread3;

public class Test7 {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        synchronized (object) {
            System.out.println("等待中");
            object.wait();
            System.out.println("等待结束");
        }
    }
}

这样在执行到object.wait()之后就一直等待下去,那么程序肯定不能一直这么等待下去了。这个时候就需要使用到了另外一个方法唤醒的方法notify()。
 

2 notify()方法

notify 方法是唤醒等待的线程

  • 方法notify()也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。
  • 如果有多个线程等待,则有线程调度器随机挑选出一个呈 wait 状态的线程。(并没有 "先来后到")
  • 在notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出同步代码块之后才会释放对象锁


 代码示例: 使用notify()方法唤醒线程

package thread3;

public class Test6 {
    private static Object object = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread waiter = new Thread(() -> {
            while (true) {
                synchronized (object) {
                    System.out.println("wait 开始");
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println("wait 结束");
                }
            }
        });
        waiter.start();

        Thread.sleep(3000);

        Thread notifier = new Thread(() -> {
            synchronized (object) {
                System.out.println("notify 之前");
                object.notify();
                System.out.println("notify 之后");
            }
        });
        notifier.start();
    }
}

3 notifyAll()方法

notify方法只是唤醒某一个等待线程. 使用notifyAll方法可以一次唤醒所有的等待线程.

package thread3;

public class Test8 {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();

        Thread wait1 = new Thread(() -> {
            synchronized (object) {
                while (true) {
                    System.out.println("wait1 开始等待");
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println("wait1 等待结束");
                }
            }
        });
        Thread wait2 = new Thread(() -> {
            synchronized (object) {
                while (true) {
                    System.out.println("wait2 开始等待");
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println("wait2 等待结束");
                }
            }
        });
        Thread wait3 = new Thread(() -> {
            synchronized (object) {
                while (true) {
                    System.out.println("wait3 开始等待");
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println("wait3 等待结束");
                }
            }
        });

        wait1.start();
        wait2.start();
        wait3.start();

        Thread notifier = new Thread(() -> {
            synchronized (object) {

                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("唤醒开始");
                object.notifyAll();
                System.out.println("唤醒结束");

            }
        });

        notifier.start();

    }
}


 

虽然是同时唤醒 3 个线程, 但是这 3 个线程需要竞争锁. 所以并不是同时执行, 而仍然是有先有后的
执行.

4.wait 和 sleep 的对比

其实理论上 wait 和 sleep 完全是没有可比性的,因为一个是用于线程之间的通信的,一个是让线程阻塞一段时间,
唯一的相同点就是都可以让线程放弃执行一段时间

1. wait 需要搭配 synchronized 使用. sleep 不需要.
2. wait 是 Object 的方法 sleep 是 Thread 的静态方法.

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

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

相关文章

ChatGPT大火,你被这四种手段“割韭菜”了嘛?

目录 黑灰产利用ChatGPT的牟利方式 1、贩卖ChatGPT账号 2、售卖ChatGPT注册工具 3、制作山寨版ChatGPT 4、制作ChatGPT教程 ChatGPT对业务安全的帮助 提升漏洞挖掘的广度和精度 提升业务安全情报的效率和广度 ChatGPT自身的安全隐患 2022年11月,ChatGPT发布时&#…

FastJson序列化和反序列化时处理数据

FastJson序列化和反序列化时处理数据序列化时处理数据反序列化时处理json数据中的值https://github.com/alibaba/fastjson/wiki/PropertyProcessable_cn https://www.cnblogs.com/ariter/p/14254342.html 序列化时处理数据 1、自定义注解用来标识json数据需要处理的属性 impo…

ESP32S3系列--SPI主机驱动详解(二)

一、目的 在上一篇《ESP32S3系列--SPI主机驱动详解(一)》我们介绍了ESP32S3的SPI外设的基本情况以及主机驱动的一些知识点,包括主机驱动的特点、总线的初始化、从设备的加入、传输模式分类等等。 本篇我们将从代码角度帮助大家进一步理解传输接口的一些细节问题。 二、实战 …

6个常见的 PHP 安全性攻击

了解常见的PHP应用程序安全威胁,可以确保你的PHP应用程序不受攻击。因此,本文将列出 6个常见的 PHP 安全性攻击,欢迎大家来阅读和学习。 1、SQL注入 SQL注入是一种恶意攻击,用户利用在表单字段输入SQL语句的方式来影响正常的SQL执…

Blender——物体的随机分布

问题描述将正方体随机分布在平面上。问题解决点击编辑-->偏好设置。在【插件】中的【物体】类型中勾选【Object: Scatter Objects】。右下的活动工具与工作区设置中就会出现【物体散列】的模块,可以调节各参数。选中正方体,按着Shift,选中…

关于 OAuth 你又了解哪些?

作者罗锦华,API7.ai 技术专家/技术工程师,开源项目 pgcat,lua-resty-ffi,lua-resty-inspect 的作者。 OAuth 的背景 OAuth,O 是 Open,Auth 是授权,也就是开放授权的意思。OAuth 始于 2006 年&a…

error when starting dev server:Error: Failed to resolve vue/compiler-sfc.

对于node 的包管理工具,我一般习惯用 yarn,但是最近使用 yarn 创建前端项目的时候出了一些问题。yarn create vite vite-project报错如下:error when starting dev server:Error: Failed to resolve vue/compiler-sfc.vitejs/plugin-vue requ…

[Arxiv 2022] A Novel Plug-in Module for Fine-Grained Visual Classification

Contents MethodPlug-in ModuleLoss functionExperimentsReferencesMethod Plug-in Module Backbone:为了帮助模型抽取出不同尺度的特征,作者在 backbone 里加入了 FPNWeakly Supervised Selector:假设 backbone 的 i i

LayUI渲染数据失败之Ajax异步交互

案例 在layui中调用jquery的ajxa,返回数据,赋值给全局变量,通过DOM渲染到页面。 //定义变量 let sale;//定义请求 $.ajax({type: "GET",url: "http://localhost:8080/product/sale",data: null,dataType: "json&q…

离散数学 课时一 命题逻辑的基本概念

1 命题 1、命题:可以判断其真值的陈述句 2、真值:真或者假(1或者0) 3、真命题:真值为真的命题 4、假命题:真值为假的命题 5、原子命题:不可以再被分解成更简单的命题 6、复合命题:由原子命题通过联结词联结…

12 Day:内存管理

前言:今天我们要完成我们操作系统的内存管理,以及一些数据结构和小组件的实现,在此之前大家需要了解我们前几天一些重要文件的内存地址存放在哪,以便我们更好的去编写内存管理模块 一,实现ASSERT断言 不知道大家有没有…

< Linux >:Linux 进程概念 (4)

目录 五、孤儿进程 六、进程优先级 6.1、基本概念 6.2、查看时实系统进程 6.3、PRI and NI 七、其他概念 四、X 状态:死亡状态 所谓进程处于 X 状态(死亡状态)代表的就是该进程已经死亡了,即操作系统可以随时回收它的资源(操作系统也可以…

代码质量与安全 | 开发人员必备的安全编码实践指南

在任何新的软件开发项目开始时,您就应该考虑软件安全。开始一个新项目或许会令人望而生畏,因为有许多的决定要做,有许多想法必须考虑清楚。通常来说,这些决定和想法包括了定义项目需求、选择正确的流程、选择正确的工具以及确保软…

QML- 导入库包语法

QML- 导入库包语法一、概述二、Import语句的语法1. Module (namespace) 模块(命名空间)导入1. 非模块命名空间的导入2. 导入到限定局部命名空间2. 路径 import1. 本地目录导入2. 远程目录3. JavaScript资源导入三、QML导入路径四、调试一、概述 import 语句其实在QML文档里面体…

Springboot扩展点之SmartInstantiationAwareBeanPostProcessor

前言这是Springboot扩展点系列的第5篇了,主要介绍一下SmartInstantiationAwareBeanPostProcessor扩展点的功能特性、和实现方式。SmartInstantiationAwareBeanPostProcessor与其他扩展点最明显的不同,就是在实际的业务开发场景中应用到的机会并不多&…

机器学习框架sklearn之特征降维

目录特征降维概念特征选择过滤式①低方差特征过滤②相关系数③主成分分析特征降维 0维 标量 1维 向量 2维 矩阵 概念 降维是指在某些限定条件下,降低随机变量(特征)个数,得到一组“不相关”主变量的过程 注:正是…

微信小程序 java 医生预约挂号答疑问询系统

生预约答疑系统用户端是基于微信小程序端,医生和管理员是基于网页后端。本系统分为用户,管理员,医生三个角色,用户的主要功能是注册登陆小程序,查看新闻资讯,查看医生列表,预约医生,…

【unity细节】关于资源商店(Package Maneger)无法下载资源问题的解决

👨‍💻个人主页:元宇宙-秩沅 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 秩沅 原创 收录于专栏:unity细节和bug ⭐关于资源商店为何下载不了的问题⭐ 文章目录⭐关于资源商店为何下载不了的问题…

鸟哥的Linux私房菜读书笔记:文件系统的简单操作

磁盘与目录的容量 现在我们知道磁盘的整体数据实在superblock区块中,但是每个个别文件的容量则在inode当中记载的. 那在命令行下面该如何显示处这几个数据呢? df:列出文件系统的整体磁盘书用量du:评估文件系统的磁盘使用量(常用在推估目录所占容量)df先来说明一下范例一所输…

网络协议(四):网络互联模型、物理层、数据链路层

网络协议系列文章 网络协议(一):基本概念、计算机之间的连接方式 网络协议(二):MAC地址、IP地址、子网掩码、子网和超网 网络协议(三):路由器原理及数据包传输过程 网络协议(四):网络互联模型、物理层、数据链路层 目录一、网…