做 Java 开发的小伙伴,对 wait 方法和 notify 方法应该都比较熟悉,这两个方法在线程通讯中使用的频率非常高,但对于 notify 方法的唤醒顺序,有很多小伙伴的理解都是错误的,有很多人会认为 notify 是随机唤醒的,但它真的是随机唤醒的吗?

带着这个疑问,我们尝试休眠 100 个线程,再唤醒 100 个线程,并把线程休眠和唤醒的顺序保持到两个集合中,最后再打印一下这两个集合,看一下它们的执行顺序,如果它们的顺序是一致的,那说明 notify 是顺序唤醒的,否则则是随机唤醒的,notify 测试代码如下:

import java.util.ArrayList;
import java.util.List; public class NotifyExample {
// 保存休眠线程的顺序
private static List<String> waitList = new ArrayList<>();
// 保存唤醒线程的顺序
private static List<String> notifyList = new ArrayList<>(); public static void main(String[] args) throws InterruptedException {
final Object lock = new Object();
// 休眠 100 个线程
for (int i = 0; i < 100; i++) {
String threadName = Integer.toString(i); // 定义线程名
new Thread(() -> {
// 获取当前执行线程的线程名
String currThreadName = Thread.currentThread().getName();
synchronized (lock) {
waitList.add(currThreadName); // 存入等待 list
try {
lock.wait(); // 休眠线程
} catch (InterruptedException e) {
e.printStackTrace();
}
notifyList.add(currThreadName); // 存储唤醒 list
}
}, threadName).start();
}
Thread.sleep(1000);
// 唤醒 100 个线程
for (int i = 0; i < 100; i++) {
synchronized (lock) {
lock.notify(); // 唤醒线程
}
}
// 打印 2 个线程列表
System.out.println("等待线程顺序:" + waitList);
System.out.println("唤醒线程顺序:" + waitList);
}
}

以上程序的执行结果如下图所示:



从上述打印的结果我们可以看出,使用 notify 并不是随机唤醒的,而是顺序唤醒的,虽然以上代码能证明这个结论,但为了更清楚的解释这个问题,我们查看了 notify 的实现源码,它的源码内容如下:



简单翻译一下上面的重点内容,notify 选择唤醒的线程是任意的,但具体的实现还要依赖于 JVM。也就是说 notify 的唤醒规则,最终取决于 JVM 厂商,不同的厂商的实现可能是不同的,比如阿里的 JVM 和 Oracle 的 JVM,关于 notify 的唤醒规则可能是不一样的。

那作为一个普通的程序员我们要研究的就是官方的 JVM 也就是 HotSpot 虚拟机,它的 notify 实现源码在 ObjectMonitor.cpp 中,具体源码如下:



DequeueWaiter 方法实现的源码如下:



从上述源码可以看出,在进行唤醒时,每次会从 _WaitSet 等待集合中获取第一个元素进行出队操作,这也说明了 notify 是顺序唤醒的。

总结

notify 唤醒线程的规则是随机唤醒还是顺序唤醒取决于 JVM 的具体实现,作为主流的 HotSpot 虚拟机中的 notify 的唤醒规则是顺序的,也就是 notify 会按照线程的休眠顺序,依次唤醒线程。

是非审之于己,毁誉听之于人,得失安之于数。

公众号:Java面试真题解析

面试合集:https://gitee.com/mydb/interview

面试突击41:notify是随机唤醒吗?的更多相关文章

  1. 面试突击39:synchronized底层是如何实现的?

    想了解 synchronized 是如何运行的?就要先搞清楚 synchronized 是如何实现? synchronized 同步锁是通过 JVM 内置的 Monitor 监视器实现的,而监视器又是 ...

  2. 《【面试突击】— Redis篇》--Redis Cluster及缓存使用和架构设计的常见问题

    能坚持别人不能坚持的,才能拥有别人未曾拥有的.关注编程大道公众号,让我们一同坚持心中所想,一起成长!! <[面试突击]— Redis篇>--Redis Cluster及缓存使用和架构设计的 ...

  3. 《【面试突击】— Redis篇》--Redis都有哪些数据类型?分别在哪些场景下使用比较合适?

    能坚持别人不能坚持的,才能拥有别人不能拥有的.关注编程大道公众号,让我们一同坚持心中所想,一起成长!! <[面试突击]— Redis篇>--Redis都有哪些数据类型?分别在哪些场景下使用 ...

  4. 《【面试突击】— Redis篇》-- Redis的线程模型了解吗?为啥单线程效率还这么高?

    能坚持别人不能坚持的,才能拥有别人未曾拥有的.关注编程大道公众号,让我们一同坚持心中所想,一起成长!! <[面试突击]— Redis篇>-- Redis的线程模型了解吗?为啥单线程效率还这 ...

  5. 《【面试突击】— Redis篇》-- Redis的主从复制?哨兵机制?

    能坚持别人不能坚持的,才能拥有别人未曾拥有的.关注左上角编程大道公众号,让我们一同坚持心中所想,一起成长!! <[面试突击]— Redis篇>-- Redis的主从复制?哨兵机制? 在这个 ...

  6. 《【面试突击】— Redis篇》-- Redis哨兵原理及持久化机制

    能坚持别人不能坚持的,才能拥有别人未曾拥有的.关注编程大道公众号,让我们一同坚持心中所想,一起成长!! <[面试突击]— Redis篇>-- Redis哨兵原理及持久化机制 在这个系列里, ...

  7. 面试突击24:为什么wait和notify必须放在synchronized中?

    在多线程编程中,wait 方法是让当前线程进入休眠状态,直到另一个线程调用了 notify 或 notifyAll 方法之后,才能继续恢复执行.而在 Java 中,wait 和 notify/noti ...

  8. 面试突击25:sleep和wait有什么区别

    sleep 方法和 wait 方法都是用来将线程进入休眠状态的,并且 sleep 和 wait 方法都可以响应 interrupt 中断,也就是线程在休眠的过程中,如果收到中断信号,都可以进行响应,并 ...

  9. Oracle DBA面试突击题

    一份ORACLE DBA面试题 一:SQL tuning 类 1:列举几种表连接方式 答: Oracle的多表连接算法有Nest Loop.Sort Merge和Hash Join三大类,每一类又可以 ...

随机推荐

  1. file_put_contents利用技巧(php://filter协议)

    Round 1 <?php $content = '<?php exit; ?>'; $content .= $_POST['txt']; file_put_contents($_P ...

  2. pytorch方面

    (113条消息) Pytorch基础:Torch.mul.Torch.mm与Torch.matmul的异同_名字填充中的博客-CSDN博客_pytorch torch.mul (111条消息) pyt ...

  3. vue解除双向绑定?

    let obj = JSON.parse(JSON.stringify(this.temp1));

  4. JS的箭头函数this作用域

    name="小刚"; let student={ name:"小明", printLog:function(){ // 这里绑定了顶层作用域,可以使用变量与方法 ...

  5. List、Set、Map 是否继承自 Collection 接口?

    List.Set 是,Map 不是.Map 是键值对映射容器,与 List 和 Set 有明显的区别, 而 Set 存储的零散的元素且不允许有重复元素(数学中的集合也是如此),List 是线性结构的容 ...

  6. DRF(Django REST Framework)框架

    目录 一.DRF中的Request 二.前戏: 关于面向对象的继承 三.初级版本 1. settings.py文件 -- 注册app 2. models.py文件 -- 创建表 3. admin.py ...

  7. vulnhub mrRobot渗透笔记

    mrRobot渗透笔记 靶机下载地址:https://www.vulnhub.com/entry/mr-robot-1,151/ kali ip 信息收集 首先依旧时使用nmap扫描靶机的ip地址 n ...

  8. Apollo模块文章

    Apollo规划模块 自动驾驶公开课 | Apollo 2.5自动驾驶规划控制 : 这篇资料比较早,但是把EM Planner和Lattice Planner这两种在资料上经常看到的算法的来历和大概原 ...

  9. 【译】客户端存储(Client-Side Storage)

    本文转载自:众成翻译译者:文蔺链接:http://www.zcfy.cc/article/660原文:http://www.html5rocks.com/en/tutorials/offline/st ...

  10. 小程序完整对接 pingpp支付

    小程序完整对接 pingpp支付 有几个先要条件: 小程序需要企业认证且开通支付功能,个人认证是无法使用支付功能的(小程序微信支付官网) pingpp 本身接入的企业服务器(即商户服务器)并不强制要求 ...