做 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