wait和notify : 避免線程餓死(以及votile內存可見性和指令重排序問題)

各位看官,大家早安午安晚安呀~~~

如果您覺得這篇文章對您有幫助的話

歡迎您一鍵三連,小編盡全力做到更好
歡迎您分享給更多人哦

今天我們來學習:wait和notify : 避免線程餓死(以及votile內存可見性和指令重排序問題)

目錄

4.volatile

4.1.保證內存可見性

4.2.:指令重排序問題(我們放到下一篇博客來講解)

5.wait,notify

5.1.wait方法的使用

5.2.notify方法的使用

6.wait和notify可以避免線程餓死


4.volatile

volatile的兩個作用(其實都是不讓編譯器做出多余的優化,對我們的程序產生bug)

1.保證內存可見性

2.解決指令重排序問題

4.1.保證內存可見性

序言:

計算機運行的程序往往要訪問數據,這些依賴的數據一般都被讀取到了內存里面(譬如我們定義的一個變量就是在內存里面)

就譬如count++;cpu先把count讀到cpu寄存器里面再進行++;

但是!? cpu其他的操作都很快,但是讀內存就比較慢(讀硬盤更慢,快慢都是相對的)

因此,為了提高效率編譯器就會做出優化,把一些要讀內存的操作優化成讀寄存器。減少讀內存的次數,就提高了整體程序的效率~~

public class Demo1 {private static int isQuit = 0;public static void main(String[] args) {Thread t1 = new Thread(() ->{while(isQuit == 0){// 一直循環啥也不不干}System.out.println("t1 退出");});Thread t2 = new Thread(()->{System.out.println(" 請輸入isQuit的值");Scanner scanner = new Scanner(System.in);isQuit = scanner.nextInt(); // 輸入不為0的值,使t1線程結束});t1.start();t2.start();}
}

我們發現我們明明輸入非0值但是t1線程就是沒結束為什么呢?

所以說volatile就是一個優化方法,我們給isQuit前面用volatile修飾就可以避免這樣的問題了

? ? ? ?在多線程環境下,編譯器對于是否要進行這樣的優化,判定不一定準.這個時候就需要程序猿通過 volatile 關鍵字,告訴編譯器,這個情況不用優化!!!優化,是算的快了,但是算的不準了)

?總之:編譯器,也不是萬能的.也會有一些自己短板的地方.此時就需要程序猿進行補充了.
只需要給 isQuit加上 volatile 關鍵字修飾,此時編譯器自然就會禁止上述優化過程。

還有另一種方式,也可以避免這樣的問題(再加上一個時間更長的操作,讓讀內存不那么慢了(相對來說)哈哈)

public class Demo1 {private static int isQuit = 0;public static void main(String[] args) {Thread t1 = new Thread(() ->{while(isQuit == 0){// 一直循環啥也不不干try {   //就多加了一個sleepThread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("t1 退出");});Thread t2 = new Thread(()->{System.out.println(" 請輸入isQuit的值");Scanner scanner = new Scanner(System.in);isQuit = scanner.nextInt(); // 輸入不為0的值,使t1線程結束});t1.start();t2.start();}
}

此時沒加 volatile, 但是給循環里加了個 slee

線程是可以退出了
加了 sleep 之后, while 循環執行速度就慢了.由于次數少了,load (讀內存)操作的開銷,就不大了.因此,優化也就沒必要進行了.
沒有觸發 load的優化,也就沒有觸發內存可見性問題了.
到底啥時候代碼有優化,啥時候沒有?也說不清楚,但是使用 volatile 還是更靠譜的選擇!!

4.2.:指令重排序問題(我們放到下一篇博客來講解)

5.wait,notify

每個線程都是獨立的執行流

想法:

由于線程之間是搶占式執行的, 因此線程之間執行的先后順序難以預知.(join是決定了線程結束的先后順序)
但是實際開發中有時候我們希望合理的協調多個線程之間的執行先后順序.

完成這個協調工作, 主要涉及到三個方法

wait() / wait(long timeout): 讓當前線程進入等待狀態.

notify() / notifyAll(): 喚醒在當前對象上等待的線程.

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

5.1.wait方法的使用

wait()方法要做的事情:

  1. 釋放當前的鎖(前提是你得有鎖,不然怎么釋放)
  2. 線程進入阻塞
  3. 線程被喚醒, 重新嘗試獲取這個鎖.

wait 要搭配 synchronized 來使用. 脫離 synchronized 使用 wait 會直接拋出異常.

  public static void main(String[] args) throws InterruptedException {Object locker = new Object();System.out.println("wait 之前");locker.wait();  //沒加鎖,會拋出異常System.out.println("wait 之后");}

wait 結束等待的條件:

1.其他線程調用該對象的 notify 方法.

2.wait 等待時間超時 (wait 方法提供一個帶有 timeout 參數的版本, 來指定等待時間).

3.其他線程調用該等待線程的 interrupted 方法, 導致 wait 拋出 InterruptedException 異常.

接下來我們看一個代碼

  public static void main(String[] args) throws InterruptedException {Object locker = new Object();System.out.println("wait 之前");locker.wait();  // 只要一般涉及阻塞的方法都會拋出異常//locker這個對象只要不notify,這個線程就一直處于waiting狀態(加了等待時間就不會了)System.out.println("wait 之后");}

運行結果以及jconsole觀察到的結果(這個程序就一直等待,死等(除非有等待時間))

5.2.notify方法的使用

我們以代碼舉例:創建三個線程都去wait,主線程notify喚醒(隨機的)

  public static void main(String[] args) throws InterruptedException {Object locker = new Object(); // 公用一個鎖對象Thread t1 = new Thread(() ->{synchronized (locker){try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("t1 醒了");});Thread t2 = new Thread(()-> {synchronized (locker){try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("t2 醒了");});Thread t3 = new Thread(()->{synchronized(locker){try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("t2 醒了");});t1.start();t2.start();t3.start();System.out.println("喚醒其中一個線程");Thread.sleep(1000);  // 確保其他線程已經處于waiting狀態synchronized(locker){locker.notify();// 也要記得加鎖呀,不然咋釋放鎖}}

結果:可以看到隨機一個線程被喚醒,其他線程在waiting狀態

如果是notifyAll方法呢?(三個線程都被喚醒了,但是第一個拿到鎖的線程如果執行的任務比較多,另外兩個線程會處于BLOCKED狀態(由于鎖競爭導致的阻塞))

6.wait和notify可以避免線程餓死

先介紹synchronized是可重入鎖,什么是可重入鎖呢?一個線程對一個對象連續加鎖兩次不會出現死鎖。就譬如這個按理說會出現死鎖但是我是可重入的所以不會形成死鎖

說到死鎖:舉一個例子()(這樣一個線程想要多把鎖,進而形成環,直接卡死就會導致死鎖)

說到環就想到哲學家問題

總結:四個成因(破壞一個就解除)

 class Singleton {private static Singleton instance = new Singleton();public static Singleton getInstance(){  // 一定要是靜態方法,不然一開始別人都拿不到這個對象,又沒辦法調用這個方法,豈不是貽笑大方return instance;}private Singleton(){} // 啥也不用干,也干了呀,把構造方法給藏起來了
}public class SingletonDemo{public static void main(String[] args) {Singleton singleton = new Singleton();}
}

上述就是wait和notify : 避免線程餓死(以及votile內存可見性和指令重排序問題)的全部內容啦

能看到這里相信您一定對小編的文章有了一定的認可。

有什么問題歡迎各位大佬指出
歡迎各位大佬評論區留言修正~~

您的支持就是我最大的動力???!!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/73954.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/73954.shtml
英文地址,請注明出處:http://en.pswp.cn/web/73954.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

HarmonyOS 介紹

HarmonyOS簡介 隨著萬物互聯時代的開啟,應用的設備底座將從幾十億手機擴展到數百億IoT設備。全新的全場景設備體驗,正深入改變消費者的使用習慣。 同時應用開發者也面臨設備底座從手機單設備到全場景多設備的轉變,全場景多設備的全新底座&am…

【視覺提示學習】3.28閱讀隨想

2109.01134 CoOp通過可學習的向量來建模提示的上下文詞匯,這些向量可以用隨機值或預訓練的詞嵌入進行初始化(見圖2)。我們提供了兩種實現方式,以處理不同性質的任務:一種是基于統一上下文(unified context…

計算機求職面試中高頻出現的經典題目分類整理

以下為計算機求職面試中高頻出現的經典題目分類整理,涵蓋技術核心與深度考察方向,答案要點已附解析思路: 一、數據結構與算法 鏈表操作 題目:反轉鏈表(迭代/遞歸實現)考察點:指針操作、遞歸思維…

uniapp選擇文件使用formData格式提交數據

1. Vue實現 在vue項目中,我們有個文件,和一些其他字段數據需要提交的時候,我們都是使用axios 設置請求頭中的Content-Type: multipart/form-data,然后new FormData的方式來進行提交。方式如下: const sendRequest = () => {const formData = new FormData()formData…

BeanDefinition和Beanfactory實現一個簡單的bean容器

目錄 什么是 Springbean 容器 設計思路 圖解 參考文章 開源地址 BeanDefinition 類 BeanFactory 類 測試類 什么是 Springbean 容器 Spring 包含并管理應用對象的配置和生命周期,在這個意義上它是一種用于承載對象的容器,你可以配置你的每個 Bea…

AI Agent開發大全第十四課-零售智能導購智能體的RAG開發理論部分

開篇 經過前面的一些課程,我們手上已經積累了各種LLM的API調用、向量庫的建立和使用、embedding算法的意義和基本使用。 這已經為我們具備了開發一個基本的問答類RAG的開發必需要素了。下面我們會來講一個基本問答類場景的RAG,零售中的“智能導購”場景。 智能導購 大家先…

向字符串添加空格

給你一個下標從 0 開始的字符串 s ,以及一個下標從 0 開始的整數數組 spaces 。 數組 spaces 描述原字符串中需要添加空格的下標。每個空格都應該插入到給定索引處的字符值 之前 。 例如,s "EnjoyYourCoffee" 且 spaces [5, 9] &#xff0…

百人會上的蔚小理與「來的剛剛好」的雷軍

這就是2025百人會上的蔚小理,努力的李斌、宣揚飛行汽車的何小鵬與大講開源的李想。那么小米汽車的模式是什么呢?站在蔚小理的肩上。 這就是2025百人會上的蔚小理,努力的李斌、宣揚飛行汽車的何小鵬與大講開源的李想。那么小米汽車的模式是什么…

解鎖Nginx路由器匹配規則

引言 Nginx 無疑是一款備受矚目的明星產品。它以其高性能、高可靠性以及出色的并發處理能力,在眾多 Web 服務器和反向代理服務器中脫穎而出 ,廣泛應用于各類網站和應用程序中。據統計,超過 30% 的網站都在使用 Nginx 作為其 Web 服務器&…

傳統策略梯度方法的弊端與PPO的改進:穩定性與樣本效率的提升

為什么傳統策略梯度方法(如REINFORCE算法)在訓練過程中存在不穩定性和樣本效率低下的問題 1. 傳統策略梯度方法的基本公式 傳統策略梯度方法的目標是最大化累積獎勵的期望值。具體來說,優化目標可以表示為: max ? θ J ( θ )…

Qwt入門

Qwt(Qt Widgets for Technical Applications)是一個用于科學、工程和技術應用的 Qt 控件庫,提供曲線圖、儀表盤、刻度尺等專業可視化組件。 1. 安裝與配置 1.1 安裝方式 源碼編譯(推薦): git clone https://github.com/qwt/qwt.git cd qwt qmake qwt.pro # 生成 Makef…

軟考《信息系統運行管理員》- 6.1 信息系統安全概述

信息系統安全的概念 信息系統安全是指保障計算機及其相關設備、設施(含網絡)的安全,運行環境的安全, 信息的安全,實現信息系統的正常運行。 信息系統安全包括實體安全、運行安全、信息安全和 人員安全等幾個部分。 影響信息系統安全的因素…

Canvas實現旋轉太極八卦圖

Canvas實現旋轉太極八卦圖 項目簡介 這是一個使用HTML5 Canvas技術實現的動態太極八卦圖,包含了旋轉動畫和鼠標交互功能。項目展示了中國傳統文化元素與現代Web技術的結合。 主要特點 動態旋轉的太極圖八卦符號的完整展示鼠標懸停暫停動畫流暢的動畫效果 技術實…

機器學習、深度學習和神經網絡

機器學習、深度學習和神經網絡 術語及相關概念 在深入了解人工智能(AI)的工作原理以及它的各種應用之前,讓我們先區分一下與AI密切相關的一些術語和概念:人工智能、機器學習、深度學習和神經網絡。這些術語有時會被交替使用&#…

打造高性能中文RAG系統:多輪對話與語義檢索的完美結合

目錄 1、引言 2、RAG系統的核心架構 3、對話理解:超越單輪問答 3.1、指代消解技術 3.2、話題跟蹤與記憶 4、混合檢索策略:兼顧精確與廣泛 4.1、向量檢索 關鍵詞檢索 4.2、重排序機制 5、性能優化:應對大規模文檔 5.1、向量量化技術…

人工智能助力數字化轉型:生成式人工智能(GAI)認證開啟新篇章

在數字化浪潮席卷全球的今天,企業正面臨著前所未有的轉型壓力與機遇。數字化轉型,這一曾經被視為“選擇題”的戰略議題,如今已演變為關乎企業生存與發展的“必答題”。在這場深刻的變革中,人工智能(AI)作為…

Windows 圖形顯示驅動開發-WDDM 2.4功能-GPU 半虛擬化(十二)

DxgkDdiQueryAdapterInfo 更新 DXGKARG_QUERYADAPTERINFO 結構已更新,以包括以下字段以支持半虛擬化: 添加了 Flags 成員,允許 Dxgkrnl 指示以下內容: 它將 VirtualMachineData 設置為指示調用來自 VM。它將 SecureVirtualMach…

iOS審核被拒:Missing privacy manifest 第三方庫添加隱私聲明文件

問題: iOS提交APP審核被拒,蘋果開發者網頁顯示二進制錯誤,收到的郵件顯示的詳細信息如下圖: 分析: 從上面信息能看出第三方SDK庫必須要包含一個隱私文件,去第三方庫更新版本。 幾經查詢資料得知,蘋果在…

馬達加斯加企鵝字幕

Antarctica 南極洲 An inhospitable wasteland 一個荒涼的不毛之地 But even here 但即使在這里 on the Earth’s frozen bottom 地球另一端的冰天雪地里 we find life 也有生命存在 And not just any life 不是別的什么生物 Penguins 而是企鵝 Joyous, frolicking 快樂的 頑皮…

愛因斯坦求和 torch

目錄 向量點積 矩陣乘法 矩陣轉置 向量轉換相機坐標系 在 Python 的科學計算庫(如 NumPy)中,einsum 是一個強大的函數,它可以簡潔地表示各種張量運算。下面是幾個不同類型的使用示例: 向量點積 向量點積是兩個向量…