线程通讯(wait方法、notify方法、notifyAll方法)

2023-11-06

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

1. 方法介绍

完成这个协调⼯作( 线程通讯 ),主要涉及到三个⽅法:
         ● wait() / wait(long timeout):让当前线程进⼊等待状态。
        ● notify():唤醒当前对象上⼀个休眠的线程(随机)。
        ● notifyAll():唤醒当前对象上的所有线程。
注意这三个⽅法都需要配合 synchronized ⼀起使⽤
这三个方法Object的内置方法,对象级别。

 

2. wait使用

 

wait 执⾏流程:
  ● 使当前执⾏代码的线程进⾏等待. (把线程放到等待队列中)
  ● 释放当前的锁(释放的是自己的锁)
  ● 满⾜⼀定条件时被唤醒, 重新尝试获取这个锁
wait 要搭配 synchronized 来使⽤. 脱离 synchronized 使⽤ wait 会直接抛出异常.
  wait 结束等待的条件:
  ● 其他线程调⽤该对象的 notify ⽅法.
  ● wait 等待时间超时 (wait ⽅法提供⼀个带有 timeout 参数的版本, 来指定等待时间).
  ● 其他线程调⽤该等待线程的 interrupted ⽅法, 导致 wait 抛出 InterruptedException 异常.
wait使用:

public class WaitDemo {
    public static void main(String[] args) {
        Object lock = new Object();

        Thread t1 = new Thread(() -> {
            System.out.println("线程1开始执行");
            try {
                synchronized (lock) {
                    System.out.println("线程1调用wait方法....");
                    // 无限期的等待状态
                    lock.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程1执行完成");
        }, "线程1");
        t1.start();
    }
}

输出:
    线程1开始执行
    线程1调用wait方法....
(程序仍在执行)

 

3. notify使用

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

public class WaitDemo2 {
    public static void main(String[] args) {
        Object lock = new Object();
        Object lock2 = new Object();

        Thread t1 = new Thread(() -> {
            System.out.println("线程1开始执行");
            try {
                synchronized (lock) {
                    System.out.println("线程1调用wait方法....");
                    // 无限期的等待状态
                    lock.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程1执行完成");
        }, "线程1");


        Thread t2 = new Thread(() -> {
            System.out.println("线程2开始执行");
            try {
                synchronized (lock) {
                    System.out.println("线程2调用wait方法....");
                    // 无限期的等待状态
                    lock.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程2执行完成");
        }, "线程2");
        t2.start();
        t1.start();
        
        // 唤醒 lock 对象上休眠的线程的(随机唤醒一个)
        Thread t4 = new Thread(() -> {
            //先让线程1,2,3先执行
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
            }
            System.out.println("线程4:开始执行");
            synchronized (lock) {
                // 发出唤醒通知
                lock.notify();
                System.out.println("线程4:执行了唤醒操作");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                }
                System.out.println("线程4:synchronized 执行完了");
            }
        }, "线程4");
        t4.start();

    }
}
输出:
    线程1开始执行
    线程2开始执行
    线程1调用wait方法....
    线程2调用wait方法....
    线程4:开始执行
    线程4:执行了唤醒操作
    线程4:synchronized 执行完了
    线程1执行完成

 

 

4. notifyAll使用

notify⽅法只是唤醒某⼀个等待线程. 使⽤notifyAll⽅法可以⼀次唤醒所有的等待线程。

public class WaitDemo3 {
    public static void main(String[] args) {
        Object lock = new Object();
        Object lock2 = new Object();

                Thread t1 = new Thread(() -> {
            System.out.println("线程1开始执行");
            try {
                synchronized (lock) {
                    System.out.println("线程1调用wait方法....");
                    // 无限期的等待状态
                    lock.wait();
                    System.out.println("线程1:恢复执行之后又进入休眠状态");
                    Thread.sleep(2000);
                    System.out.println("线程1执行完成");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "线程1");

                Thread t2 = new Thread(() -> {
            System.out.println("线程2开始执行");
            try {
                synchronized (lock) {
                    System.out.println("线程2调用wait方法....");
                    // 无限期的等待状态
                    lock.wait();
                    System.out.println("线程2:恢复执行之后又进入休眠状态");
                    Thread.sleep(2000);
                    System.out.println("线程2执行完成");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "线程2");

                Thread t3 = new Thread(() -> {
            System.out.println("线程3开始执行");
            try {
                synchronized (lock) {
                    System.out.println("线程3调用wait方法....");
                    // 无限期的等待状态
                    lock.wait();
                    System.out.println("线程3:恢复执行之后又进入休眠状态");
                    Thread.sleep(2000);
                    System.out.println("线程3执行完成");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "线程3");
        t2.start();
        t1.start();
        t3.start();

        // 唤醒 lock 对象上休眠的线程的(随机唤醒一个)
        Thread t4 = new Thread(() -> {
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
            }
            System.out.println("线程4:开始执行");
            synchronized (lock) {
                // 发出唤醒通知
                lock.notifyAll();
                lock.notifyAll();
                System.out.println("线程4:执行了唤醒操作");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                }
                System.out.println("线程4:synchronized 执行完了");
            }
        }, "线程4");
        t4.start();

    }
}
输出:
    线程2开始执行
    线程2调用wait方法....
    线程3开始执行
    线程1开始执行
    线程3调用wait方法....
    线程1调用wait方法....
    线程4:开始执行
    线程4:执行了唤醒操作
    线程4:synchronized 执行完了
    线程1:恢复执行之后又进入休眠状态
    线程1执行完成
    线程3:恢复执行之后又进入休眠状态
    线程3执行完成
    线程2:恢复执行之后又进入休眠状态
    线程2执行完成
修改线程3中的lock为lock2

try {
                synchronized (lock2) {
                    System.out.println("线程3调用wait方法....");
                    // 无限期的等待状态
                    lock2.wait();
                    System.out.println("线程3:恢复执行之后又进入休眠状态");
                    Thread.sleep(2000);
                    System.out.println("线程3执行完成");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

输出:
    线程2开始执行
    线程1开始执行
    线程3开始执行
    线程2调用wait方法....
    线程3调用wait方法....
    线程1调用wait方法....
    线程4:开始执行
    线程4:执行了唤醒操作
    线程4:synchronized 执行完了
    线程1:恢复执行之后又进入休眠状态
    线程1执行完成
    线程2:恢复执行之后又进入休眠状态
    线程2执行完成

 由上述两个代码可以得出,notifyAll 并不是唤醒所有的wait 等待的线程,而是唤醒当前对象处于wait 等待的所有线程。

5. 注意事项

        1. wait / notify / notifyAll 必须要配合synchronized一起执行。
        2. wait / notify / notifyAll 进行 synchronized 加锁,一定要使用同一个·对象进行加锁,不然会报错。
        3. 当调用了notify /  notifyAll 之后,程序并不会立即恢复执行,而是尝试获取锁,只有得到锁之后才能继续执行。
        4. notifyAll 并不是唤醒所有的wait 等待的线程,而是唤醒当前对象处于wait 等待的所有线程。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

线程通讯(wait方法、notify方法、notifyAll方法) 的相关文章

  • 何时/为何使用/定义接口[重复]

    这个问题在这里已经有答案了 可能的重复 何时最好使用 java 中的接口 https stackoverflow com questions 2586389 when best to use an interface in java Hi
  • 如何从 WifiP2pDeviceList 获取 wifi direct 设备名称

    我想在执行请求对等点时获取 wi fi direct 名称 这是我的代码 if WifiP2pManager WIFI P2P PEERS CHANGED ACTION equals action Log d tag success dis
  • JavaFX Platform.runLater 的使用以及从不同线程访问 UI

    我有几个问题Platform runLater 我有一个 JavaFX 应用程序类 在这个类中 我运行一个线程 该线程从网络套接字读取数据 现在当我创建一个新的Stage在线程内部 系统抛出异常 JavaFX 事件调度程序线程和我的网络读取
  • 设置 SWT Shell 的默认字体

    有没有办法为整个 Shell 设置默认字体 以便任何新控件都将使用相同的字体 看来现在我必须为我创建的每个控件设置字体 这导致了太多的冗余 默认使用的字体由平台选择 请参阅中的其他信息 类字体 SWT 标准小部件工具包 http book
  • 实现与扩展:何时使用?有什么不同?

    请用易于理解的语言进行解释或提供某些文章的链接 extends is for 延伸一类 implements is for 实施一个接口 接口和常规类之间的区别在于 在接口中您不能实现任何声明的方法 只有 实现 接口的类才能实现方法 C 中
  • 要打乱的键值(整数、字符串)列表的最佳结构

    我需要在 Java 中实现一个结构 它是一个键值列表 类型为整数 字符串 并且我想对其进行洗牌 基本上 我想做类似的事情 public LinkedHashMap
  • 如何在 Struts 2 OGNL 中将参数传递给方法调用

    我想使用属性作为对象方法的参数
  • 如何制作无限的jscrollpane?

    我之前已经实现过拖动滚动 但是创建无限滚动窗格的最佳方法是什么 当然不会有任何滚动条 我将实现拖动滚动 我想做的是在无限表面上实现动态加载 EDIT 当然 它实际上不会是无限的 我想问如何伪造它 您可以执行以下操作 AdjustmentCl
  • 在 eclipse 之外将 Spring MVC 应用程序部署到 tomcat 的幕后会发生什么?

    我猜想使用像 eclipse 这样很棒的 IDE 的一个缺点是你会忽略应用程序幕后发生的事情 我是一名 Ruby 开发人员 所以不是一名 Java 老手 所以我一直在用 java 编写一个项目 并使用 spring 框架进行 IOC 和 M
  • 如何使用 Guava 连接字符串?

    我写了一些代码来连接字符串 String inputFile for String inputLine list inputFile inputLine trim 但我不能使用 连接 所以我决定使用 Guava 所以我需要使用Joiner
  • 战争库中的罐子爆炸

    我们可以将分解的 jar 文件放入 war web inf 库中吗 它在 JBOSS 4 2 中对我不起作用 我收到以下错误并且无法部署应用程序 Caused by javax management RuntimeOperationsExc
  • 从命令行运行 Maven 插件的语法是什么。

    我看到这里已经有人问过这个问题 如何从命令行执行maven插件 https stackoverflow com questions 12930656 how to execute maven plugin from command line
  • Selenium - 等待网络流量

    我们将 Selenium 与 Java API 和一些 Javascript 用户扩展一起使用 我们在应用程序中使用了大量 AJAX 调用 我们的许多测试随机失败 因为有时 AJAX 调用完成得比其他时候慢 因此页面未完全加载 我们通过等待
  • H2 - (相当)长的 INSERT 失败,错误 42000

    H2 内存中 插入 错误 42000 尝试过版本 1 4 196 1 4 197 1 4 199 我还尝试在 H2 服务器 本地 上执行 INSERT 也失败 给出错误的行 抱歉 但出于安全原因 我无法生成更多 INSERT INTO tb
  • 如果抛出RuntimeException,是否可以将其作为异常捕获?

    如果我有一个try抛出一个块RuntimException子类 可以是后续的catch块将其捕获为Exception 具体来说 public class MyAppException extends RuntimeException In
  • 方法签名中带或不带synchronized关键字的方法具有相同的字节码

    对于以下 2 个类 获得相同的 Java 字节码 java版本 java 版本 1 8 0 181 Java TM SE 运行时环境 构建 1 8 0 181 b13 Java HotSpot TM 64 位服务器 VM 内部版本 25 1
  • JMockit - 初始化问题

    当我使用以下测试时 我收到警告 警告 JMockit 是按需初始化的 这可能会导致某些测试失败 请检查文档以获取更好的初始化方法 这是我的测试实现 package test import static mockit Mockit impor
  • 如何从spark中的hbase表中获取所有数据

    我在 hbase 中有一个大表 名称为 UserAction 它具有三个列族 歌曲 专辑 歌手 我需要从 歌曲 列族中获取所有数据作为 JavaRDD 对象 我尝试了这段代码 但效率不高 有更好的解决方案来做到这一点吗 static Spa
  • 如何使用剪辑来减少绘画时间?

    我正在尝试使用 Clip 来减少 CPU 负载 但剪辑在屏幕上留下了一些我似乎无法摆脱的垃圾 另外 打开和关闭剪辑似乎对 CPU 负载没有影响 在任一情况下 大部分时间似乎都花在重绘管理器和绘制缓冲图像上 import static jav
  • 我找不到 IntelliJ 快捷方式

    我使用 vim 一段时间 我知道有一个 intellij vim 插件 我很好奇内置的 IntelliJ 文本导航存在什么 如何打开实时模板来创建模板 如何查看以 tr 开头的现有模板列表 如何进行全局搜索并在当前文档中进行搜索 然后转到下

随机推荐

  • JPA中所有findBy语法规则(举例)

    JPA中findBy基本语法规则 1 首先先新建一个数据库 名字叫做jpatest 2 新建一个SpringBoot项目 如果新手还不会 请先阅读idea中如何快速创建SpringBoot项目 这边需要引入jpa mysql web的相关依
  • 深刻理解Linux进程间通信(IPC)

    http www cnblogs com andtt articles 2136279 html 0 序 1 管道 1 1 管道概述及相关API应用 1 2 有名管道概述及相关API应用 1 3 小结 1 4 参考资料 2 信号 上 2 1
  • 详解DenseNet(密集连接的卷积网络)

    前言 在计算机视觉领域 卷积神经网络 CNN 已经成为最主流的方法 比如最近的GoogLenet VGG 19 Incepetion等模型 CNN史上的一个里程碑事件是ResNet模型的出现 ResNet可以训练出更深的CNN模型 从而实现
  • C++中enum的大小

    关于枚举类型所占内存的大小 书里对枚举大小的定义是 sizeof枚举是sizeof某类可以包含枚举range的整型 并且不会大于sizeof int 也就是说枚举大小不一定等于sizeof int 转载请尊重原创 保留相关链接本文来自多宝平
  • MySQL限制数据的小数位数——DECIMAL类型

    DECIMAL简介 DECIMAL从MySQL 5 1引入 列的声明语法是DECIMAL M D NUMERIC与DECIMAL同义 如果字段类型定义为NUMERIC 则将自动转成DECIMAL 对于声明语法DECIMAL M D 自变量的
  • 蓝斯登定律(转载)

    给员工快乐的工作环境 蓝斯登定律 给员工快乐的工作环境 跟一位朋友一起工作 远较在父亲之下工作有趣得多 提出者 美国管理学家蓝斯登 点评 可敬不可亲 终难敬 有权没有威 常失权 编辑 从案例中体会蓝斯登定律 连续20年保持赢利的美国西南航空
  • 机器学习特征工程

    特征工程 目录 特征工程 1 数据预处理 1 1数据无量纲化 1 2缺失值处理 1 3处理分类型特征 编码与哑变量 1 4处理连续型特征 二值化与分段 1 5数据变换 总结 2 特征选择 2 1 Filter 2 1 1 方差选择法 2 1
  • Qt Creator打开CMake管理的Quick工程,并调试qml

    文章目录 前言 一 需求背景 二 遇到的问题 三 解决方案 四 Demo 提示 以下是本篇文章正文内容 下面案例可供参考 一 需求背景 1 需要对Qml程序进行调试 2 用CMake管理工程文件 3 能用Qt Creator或者VS进行开发
  • 数据库之postgresql库锁表解锁

    1 检索出死锁进程的ID SELECT FROM pg stat activity WHERE datname 死锁的数据库ID 检索出来的字段中 wating 字段 数据为t的那条 就是死锁的进程 找到对应的 procpid 列的值 2
  • LWIP UDP 编程

    一 udp c实现的函数 1 void udp input struct pbuf p struct netif inp 说明 处理接收到的udp数据包 参数 p数据包缓存区 inp网络接口 2 err t udp send struct
  • 树的层次遍历(广度优先搜索BFS)

    解题思路 采用树的层次遍历的方式 在图中叫广度优先遍历 使用队列取存储待遍历的节点 程序的结束就是队列为空 1 整体上 出队列的节点指向队列中的0号元素 比如1遍历完成之后2 3进队列 2出队列 那么2的next指向队列中的0号元素即可 但
  • MySQL Command Line 语句无效

    在cmd中输入sql语句回车后没反应 只有 gt 提示继续输入 如下图 原因 结尾未输入分号 以结束sql语句 在后面添加分号 英文输入法 即可
  • .NET开发必看资料53个+经典源码77个

    NET开发必看资料53个 经典源码77个 2012 07 01 23 12 4546人阅读 评论 1 收藏 举报 net asp net c java winform 编程 简单描述 为大家整理了下载中心 net资料 都是比较热的 好评率比
  • MIC—BIAS

    MIC BIAS为麦克的直流偏置电压 1 你说的mic bias应该说的是主板上麦克的偏置电压 偏置电压是由英文bias voltage翻译得到的 2 在电子技术课程中 我们知道 由三极管组成的放大电路能够放大一定范围的交流信号 但前提是需
  • 中望软件笔试

    文章目录 前言 一 判断一点是否在三角形中 题目简介 一 面积 代码实现 二 向量 代码实现 二 求矩阵中的最小步数 题目简介 代码实现 总结 前言 简单记录一下笔试情况 一 判断一点是否在三角形中 题目简介 输入四个二维坐标 前三个点表示
  • #vue# 接口封装!超详细超简单的请求接口方法

    在前端开发中 请求接口 是必不可少的一个环节 请求接口 通俗来说 就是我们通过请求服务器的数据 来达到响应式地渲染数据 那如何请求接口 才会更高效且简单呢 以下方法可以参考借鉴借鉴 一 接口文档目录位置 可以参考这个目录 进行文档的存放 二
  • C#报错: The maximum string content length quota (8192) has been exceeded while reading XML data

    C 客户端 调用别家的webservice 返回信息报错 摘取其中重要的如下 The maximum string content length quota 8192 has been exceeded while reading XML
  • MySQL索引数据结构hash解析

    Hash 对索引的key进行一次hash计算就可以定位出数据存储的位置 很多时候Hash索引要比B 树索引更高效 仅能满足 IN 不支持范围查询 哈希表这种结构适用于只有等值查询的场景 比如 Memcached 及其他一些 NoSQL 引擎
  • Jeecg-boot手把手基础部署教程(从零开始)【软件安装+环境安装idea版】

    这个是jeecg boot安装教程 现场排雷版 预计2020年4月1日完成整个部署及排雷 小白手把手专用 系统环境 x64的win10 虚拟机 virtualBox 因为紧急情况下 可以直接部署在vm主机上 基础技术要点 http jeec
  • 线程通讯(wait方法、notify方法、notifyAll方法)

    目录 1 方法介绍 2 wait使用 3 notify使用 4 notifyAll使用 5 注意事项 由于线程之间是抢占式执 的 因此线程之间执 的先后顺序难以预知 但是实际开发中有时候我们希望合理的协调多个线程之间的执 先后顺序 1 方法