Java中notify是順序喚醒還是隨機(jī)喚醒的
前言:
做 Java 開發(fā)的小伙伴,對(duì) wait 方法和 notify 方法應(yīng)該都比較熟悉,這兩個(gè)方法在線程通訊中使用的頻率非常高,但對(duì)于 notify 方法的喚醒順序,有很多小伙伴的理解都是錯(cuò)誤的,有很多人會(huì)認(rèn)為 notify 是隨機(jī)喚醒的,但它真的是隨機(jī)喚醒的嗎?
帶著這個(gè)疑問,我們嘗試休眠 100 個(gè)線程,再喚醒 100 個(gè)線程,并把線程休眠和喚醒的順序保持到兩個(gè)集合中,最后再打印一下這兩個(gè)集合,看一下它們的執(zhí)行順序,如果它們的順序是一致的,那說明 notify 是順序喚醒的,否則則是隨機(jī)喚醒的,
notify 測(cè)試代碼如下:
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 個(gè)線程 for (int i = 0; i < 100; i++) { String threadName = Integer.toString(i); // 定義線程名 new Thread(() -> { // 獲取當(dāng)前執(zhí)行線程的線程名 String currThreadName = Thread.currentThread().getName(); synchronized (lock) { waitList.add(currThreadName); // 存入等待 list try { lock.wait(); // 休眠線程 } catch (InterruptedException e) { e.printStackTrace(); } notifyList.add(currThreadName); // 存儲(chǔ)喚醒 list } }, threadName).start(); } Thread.sleep(1000); // 喚醒 100 個(gè)線程 for (int i = 0; i < 100; i++) { synchronized (lock) { lock.notify(); // 喚醒線程 } } // 打印 2 個(gè)線程列表 System.out.println("等待線程順序:" + waitList); System.out.println("喚醒線程順序:" + waitList); } }
以上程序的執(zhí)行結(jié)果如下圖所示:
從上述打印的結(jié)果我們可以看出,使用 notify 并不是隨機(jī)喚醒的,而是順序喚醒的,雖然以上代碼能證明這個(gè)結(jié)論,但為了更清楚的解釋這個(gè)問題,我們查看了 notify 的實(shí)現(xiàn)源碼,
它的源碼內(nèi)容如下:
簡(jiǎn)單翻譯一下上面的重點(diǎn)內(nèi)容,notify 選擇喚醒的線程是任意的,但具體的實(shí)現(xiàn)還要依賴于 JVM。也就是說 notify 的喚醒規(guī)則,最終取決于 JVM 廠商,不同的廠商的實(shí)現(xiàn)可能是不同的,比如阿里的 JVM 和 Oracle 的 JVM,關(guān)于 notify 的喚醒規(guī)則可能是不一樣的。
那作為一個(gè)普通的程序員我們要研究的就是官方的 JVM 也就是 HotSpot 虛擬機(jī),它的 notify 實(shí)現(xiàn)源碼在 ObjectMonitor.cpp 中,
具體源碼如下:
DequeueWaiter 方法實(shí)現(xiàn)的源碼如下:
從上述源碼可以看出,在進(jìn)行喚醒時(shí),每次會(huì)從 _WaitSet 等待集合中獲取第一個(gè)元素進(jìn)行出隊(duì)操作,這也說明了 notify 是順序喚醒的。
總結(jié):
notify 喚醒線程的規(guī)則是隨機(jī)喚醒還是順序喚醒取決于 JVM 的具體實(shí)現(xiàn),作為主流的 HotSpot 虛擬機(jī)中的 notify 的喚醒規(guī)則是順序的,也就是 notify 會(huì)按照線程的休眠順序,依次喚醒線程。
到此這篇關(guān)于Java中notify是順序喚醒還是隨機(jī)喚醒的的文章就介紹到這了,更多相關(guān)Java notify喚醒規(guī)則內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot學(xué)習(xí)之全局異常處理設(shè)置(返回JSON)
本篇文章主要介紹了SpringBoot學(xué)習(xí)之全局異常處理設(shè)置(返回JSON),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-02-02你不知道的 IDEA Debug調(diào)試小技巧(小結(jié))
這篇文章主要介紹了你不知道的 IDEA Debug調(diào)試小技巧(小結(jié)),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10解決SpringBoot啟動(dòng)過后不能訪問jsp頁(yè)面的問題(超詳細(xì))
這篇文章主要介紹了解決SpringBoot啟動(dòng)過后不能訪問jsp頁(yè)面的問題,文中通過示例代碼介紹的非常詳細(xì),有需要的朋友可以參考一下,希望對(duì)你有所幫助。2020-05-05jstl之map,list訪問遍歷以及el表達(dá)式map取值的實(shí)現(xiàn)
下面小編就為大家?guī)硪黄猨stl之map,list訪問遍歷以及el表達(dá)式map取值的實(shí)現(xiàn)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-03-03Springboot整合FreeMarker的實(shí)現(xiàn)示例
本文主要介紹了Springboot整合FreeMarker的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02詳解spring集成mina實(shí)現(xiàn)服務(wù)端主動(dòng)推送(包含心跳檢測(cè))
本篇文章主要介紹了詳解spring集成mina實(shí)現(xiàn)服務(wù)端主動(dòng)推送(包含心跳檢測(cè)),具有一定的參考價(jià)值,與興趣的可以了解一下2017-09-09解析springboot整合谷歌開源緩存框架Guava Cache原理
本文主要為大家解析了springboot整合谷歌開源緩存框架Guava Cache的原理以及在實(shí)際開發(fā)過程中的使用,附含源碼,有需要的朋友可以參考下2021-08-08java中List數(shù)組用逗號(hào)分隔開轉(zhuǎn)成字符串2種方法
在我們?nèi)粘i_發(fā)中,在前后端交互的時(shí)候會(huì)遇到多個(gè)id或其他字段存放到一個(gè)字段中,這時(shí)我們會(huì)遇到一個(gè)List(集合)---->String(單個(gè)字段),這篇文章主要給大家介紹了關(guān)于java中List數(shù)組用逗號(hào)分隔開轉(zhuǎn)成字符串的2種方法,需要的朋友可以參考下2023-10-10Spring?Boot?整合持久層之Spring Data JPA
在介紹Spring Data JPA的時(shí)候,我們首先認(rèn)識(shí)下Hibernate。Hibernate是數(shù)據(jù)訪問解決技術(shù)的絕對(duì)霸主,使用O/R映射技術(shù)實(shí)現(xiàn)數(shù)據(jù)訪問,O/R映射即將領(lǐng)域模型類和數(shù)據(jù)庫(kù)的表進(jìn)行映射,通過程序操作對(duì)象而實(shí)現(xiàn)表數(shù)據(jù)操作的能力,讓數(shù)據(jù)訪問操作無(wú)須關(guān)注數(shù)據(jù)庫(kù)相關(guān)的技術(shù)2022-08-08