山東大學軟件學院項目實訓-創新實訓-基于大模型的旅游平臺(二十)- JUC(6)

目錄

wait , notify

wait vs sleep

正確使用方法

同步保護性暫停

join的源碼

Future

異步生產者/消費者模型

定義

Park & Unpark

原理


wait , notify

小故事小南需要煙才能工作,但它又要占這鎖讓別人無法進來。那么這個時候開一個waitSet相當于就是休息室讓小南進去。并且釋放鎖。如果煙到了,那么notify小南就能夠繼續工作了。

Blocked和Waiting區別其實就是waiting是釋放了鎖,blocked是沒有鎖waiting被notify之后仍然需要進入到entrylist進行等待。

  @Slf4j(topic = "c.TestWaitNotify")public class Test {?// 鎖對象final static Object obj = new Object();?public static void main(String[] args) throws InterruptedException {?new Thread(() -> {synchronized (obj) {log.debug("執行....");try {obj.wait(); // 讓線程在obj上一直等待下去} catch (InterruptedException e) {e.printStackTrace();}log.debug("其它代碼....");}},"t1").start();?new Thread(() -> {synchronized (obj) {log.debug("執行....");try {obj.wait(); // 讓線程在obj上一直等待下去} catch (InterruptedException e) {e.printStackTrace();}log.debug("其它代碼....");}},"t2").start();?// 主線程兩秒后執行Thread.sleep(5000);log.debug("喚醒 obj 上其它線程");synchronized (obj) {// ? ? ? ? ?  obj.notify(); // 喚醒obj上一個線程obj.notifyAll(); // 喚醒obj上所有等待線程}}}
  20:17:53.579 [t1] DEBUG c.TestWaitNotify - 執行....20:17:53.581 [t2] DEBUG c.TestWaitNotify - 執行....20:17:58.584 [main] DEBUG c.TestWaitNotify - 喚醒 obj 上其它線程20:17:58.584 [t2] DEBUG c.TestWaitNotify - 其它代碼....20:17:58.584 [t1] DEBUG c.TestWaitNotify - 其它代碼....?進程已結束,退出代碼0

wait vs sleep

sleep:Thread調用,靜態方法,而且不會釋放鎖

wait:所有obj,但是要配合synchronize使用,可以釋放鎖

sleep在睡眠時,不會釋放鎖,wait會釋放對象鎖

通常鎖會加上final防止被修改

正確使用方法

小南需要煙才能工作,如果是使用sleep不釋放鎖,那么其他需要等待干活的人就會干等著,等煙來。但是wait可以讓小南釋放鎖,讓其他線程工作,并且喚醒小南

  @Slf4j(topic = "c.TestCorrectPosture")public class Test {static final Object room = new Object();?// 有無煙static boolean hasCigarette = false;static boolean hasTakeout = false;?public static void main(String[] args) throws InterruptedException {new Thread(() -> {synchronized (room) {log.debug("有煙沒?[{}]", hasCigarette);if (!hasCigarette) {log.debug("沒煙,先歇會!");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}log.debug("有煙沒?[{}]", hasCigarette);if (hasCigarette) {log.debug("可以開始干活了");}}}, "小南").start();?for (int i = 0; i < 5; i++) {new Thread(() -> {synchronized (room) {log.debug("可以開始干活了");}}, "其它人").start();}?Thread.sleep(1000);// 送煙線程new Thread(() -> {synchronized (room) {hasCigarette = true;log.debug("煙到了噢!");}}, "送煙的").start();}?}
  20:32:22.014 [小南] DEBUG c.TestCorrectPosture - 有煙沒?[false]20:32:22.019 [小南] DEBUG c.TestCorrectPosture - 沒煙,先歇會!20:32:24.024 [小南] DEBUG c.TestCorrectPosture - 有煙沒?[false]20:32:24.024 [送煙的] DEBUG c.TestCorrectPosture - 煙到了噢!20:32:24.024 [其它人] DEBUG c.TestCorrectPosture - 可以開始干活了20:32:24.024 [其它人] DEBUG c.TestCorrectPosture - 可以開始干活了20:32:24.024 [其它人] DEBUG c.TestCorrectPosture - 可以開始干活了20:32:24.024 [其它人] DEBUG c.TestCorrectPosture - 可以開始干活了20:32:24.024 [其它人] DEBUG c.TestCorrectPosture - 可以開始干活了?進程已結束,退出代碼0

存在的問題 :

  1. 其它干活的線程,都要一致阻塞,效率低

  2. 就算煙提前送到,也無法立刻醒來

  3. 送煙加上鎖之后,相當于門一直鎖著,煙送不進去

改進 :

  @Slf4j(topic = "c.TestCorrectPosture")public class Test {static final Object room = new Object();?// 有無煙static boolean hasCigarette = false;static boolean hasTakeout = false;?public static void main(String[] args) throws InterruptedException {new Thread(() -> {synchronized (room) {log.debug("有煙沒?[{}]", hasCigarette);if (!hasCigarette) {log.debug("沒煙,先歇會!");try {room.wait();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("有煙沒?[{}]", hasCigarette);if (hasCigarette) {log.debug("可以開始干活了");}}}, "小南").start();?for (int i = 0; i < 5; i++) {new Thread(() -> {synchronized (room) {log.debug("可以開始干活了");}}, "其它人").start();}?Thread.sleep(1000);// 送煙線程new Thread(() -> {synchronized (room) {hasCigarette = true;log.debug("煙到了噢!");room.notify(); ? ?// 叫醒小南線程}}, "送煙的").start();}?}

存在問題

會不會有其他線程在等待著鎖?如果是那么會不會喚醒錯了線程?(虛假喚醒)

解決 :

可以通過while多次判斷條件是否成立,直接使用notifyAll來喚醒所有的線程。然后線程被喚醒之后先再次判斷條件是否成立,成立那么往下面執行,如果不成立那么繼續執行wait。

  while (!hasCigarette) {log.debug("沒煙,先歇會!");try {room.wait();} catch (InterruptedException e) {e.printStackTrace();}}

正確使用 :

  synchronized(lock){while(條件不成立){lock.wait();}   // 干活}?// 另一個線程synchronized(lock){lock.notifyAll();}

同步保護性暫停

定義

  • t1需要t2的結果,那么就可以通過一個中間對象guardedObject來充當這個中間商,t2執行完就發送消息到obj,然后obj交給t1

  • 如果是不斷發送結果那么可以使用消息隊列

  • 要等待所以是同步

  • join和future就是用的這個原理

  public class Test {public static void main(String[] args) {GuaObj guaObj = new GuaObj();Thread thread = new Thread(() -> {System.out.println("鎖住,等待結果");guaObj.get(2000);System.out.println("解鎖");}, "t1");thread.start();??Thread thread1 = new Thread(() -> {System.out.println("先睡兩秒");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("解鎖,設置對象");guaObj.set(new Object());}, "t2");thread1.start();}}?class GuaObj{// 結果public Object response;?// 獲取結果// timeout表示最多等多久public Object get(long timeout){synchronized (this){// 開始時間long cur = System.currentTimeMillis();// 經歷的時間long paseTime=0;while(response==null){try {// 這一輪應該等的時間long waitTime=timeout-paseTime;//超時就不等了if(waitTime<=0) break;this.wait(waitTime);paseTime=System.currentTimeMillis()-cur;} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("等待結束");return response;}}?// 產生結果public void set(Object response){synchronized (this){this.response=response;this.notifyAll();}}}
  鎖住,等待結果先睡兩秒解鎖,設置對象等待結束解鎖?進程已結束,退出代碼0
  • 需要記錄超時的時間,并且重新設置waittime,原因是可能會有虛假喚醒,那么這個時候超時時間不是timeout而是timeout-passedTime,也就是線程執行的時間。

  • 如果超時的話,那么就會自動結束

join的源碼

  public final synchronized void join(long millis)throws InterruptedException {//一開始的時間long base = System.currentTimeMillis();//線程執行的時間long now = 0;?//如果是<0那么就拋出異常if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");}?//如果是0那么就一直等待線程執行完,isAlive是否生存if (millis == 0) {while (isAlive()) {wait(0);}} else {//timeout超時那么就結束while (isAlive()) {long delay = millis - now;if (delay <= 0) {break;}wait(delay);now = System.currentTimeMillis() - base;}}}

Future

相當于就是一個信箱,里面裝了很多GuardObject對象,線程可以通過對應的地址訪問對象獲取結果

異步生產者/消費者模型

定義

相當于就是生產者給隊列生產結果,消費者負責處理結果

  • 不需要一一對應

  • 平衡資源

  • 消息隊列有容量控制

  • 阻塞隊列控制結果出隊列

  public class Test {public static void main(String[] args) {MesageQueue queue = new MesageQueue(2);?for (int i = 0; i < 3; i++) {int id = i;new Thread(() -> {queue.set(new Message(id, "值" + id));},"生產者" + i).start();}?new Thread(() -> {while(true){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}Message message = queue.take();}}, "消費者").start();}}??@Slf4jclass MesageQueue{//存消息的集合private LinkedList<Message> list = new LinkedList();// 消息容量private int capacity;?public MesageQueue(int capacity){this.capacity = capacity;}?// 獲取消息public ?Message take()  {// 檢查隊列是否為空synchronized (list){while(list.isEmpty()){try {log.debug("隊列為空,消費者線程等待");list.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}Message message = list.removeFirst();log.debug("已經消費了消息 {}",message);list.notifyAll();return message;}}?// 存入消息public void set(Message message) {// 檢查是不是滿了synchronized (list){while(list.size() == capacity){try {log.debug("隊列已滿,生產者線程等待");list.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}list.addLast(message);log.debug("已經生產了消息 {}",message);list.notifyAll();}}}?// 消息類final class ?Message{private int id;private Object value;?public Message(int id, Object value){this.id = id;this.value = value;}?public int getId() {return id;}?public Object getValue() {return value;}?@Overridepublic String toString() {return "Message{" +"id=" + id +", value=" + value +'}';}}
  12:58:24.373 [生產者1] DEBUG MesageQueue - 已經生產了消息 Message{id=1, value=值1}12:58:24.375 [生產者2] DEBUG MesageQueue - 已經生產了消息 Message{id=2, value=值2}12:58:24.377 [生產者0] DEBUG MesageQueue - 隊列已滿,生產者線程等待12:58:25.371 [消費者] DEBUG MesageQueue - 已經消費了消息 Message{id=1, value=值1}12:58:25.371 [生產者0] DEBUG MesageQueue - 已經生產了消息 Message{id=0, value=值0}12:58:26.386 [消費者] DEBUG MesageQueue - 已經消費了消息 Message{id=2, value=值2}12:58:27.397 [消費者] DEBUG MesageQueue - 已經消費了消息 Message{id=0, value=值0}12:58:28.405 [消費者] DEBUG MesageQueue - 隊列為空,消費者線程等待

Park & Unpark

與wait和notify的區別

  • 不需要與monitor一起使用

  • 可以精準喚醒和阻塞線程

  • 可以先unpark,但是不能先notify。但是unpark之后park不起作用。

原理

①park,先去到counter里面判斷是不是0,如果是那么就讓線程進入隊列。接著就是把counter設置為0

②unpark,那么喚醒線程,恢復運行,并且把counter設置為1

③先unpark后park,那么就unpark補充counter為1,那么park判斷counter是1,認為還有體力可以繼續執行。

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

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

相關文章

一文講解——Java多態

目錄 一、什么是多態&#xff1f;二、轉型向上轉型向下轉型 三、方法覆蓋與方法重載四、綁定動態綁定靜態綁定 五、理解多態 一、什么是多態&#xff1f; 多態的詞組字面意思是&#xff1a; 某種事物多種形態。 但是對于我們學習Java 的程序原來說&#xff0c;就不不能簡單這樣…

springboot中線程池的使用

一、概念 線程池就是將多個線程對象放入一個池子里面&#xff0c;例如一個池塘&#xff0c;線程池就是這個池塘&#xff0c;池塘里面的魚就是線程池中的多個線程對象。1. 每一個線程&#xff0c;在一段時間內只能執行一個任務。2. 線程池中的各個線程是可以重復使用的。 二、創…

2024年內外貿一體化融合發展(長沙)交易會 ?辦公文具、禮品工藝品展

2024年內外貿一體化融合發展&#xff08;長沙&#xff09;交易會 辦公文具、禮品工藝品展 時間&#xff1a;2024年7月12-14日 地點&#xff1a;湖南國際會展中心&#xff08;芒果館&#xff09; 湖南省商廳 關于邀請參加2024內外貿一體化融合發展&#xff08;長沙&#xf…

Weblogic XML反序列化漏洞 [CVE-2017-10271]

漏洞環境搭建請參考 http://t.csdnimg.cn/i11e2 漏洞原理 Weblogic的wls security組件對外提供webservice服務&#xff0c;wls security組件使用了xmldecoder來解析用戶傳入的xml數據&#xff0c;如果用戶進行xml惡意數據的構造&#xff0c;即可觸發反序列化漏洞 漏洞版本 O…

簡述vue的實現原理

Vue.js 的實現原理可以概括為以下幾個方面&#xff1a; 響應式系統&#xff1a; Vue 的核心是其響應式系統。當 Vue 實例被創建時&#xff0c;它會遍歷 data 對象中的所有屬性&#xff0c;并使用 Object.defineProperty 方法將其轉換為 getter 和 setter。當 data 中的屬性發生…

python:如何創建簡單的流媒體服務器來播放.flv文件

要在Python中創建一個簡單的流媒體服務器來播放FLV&#xff08;Flash Video&#xff09;文件&#xff0c;你通常需要一個HTTP服務器&#xff0c;該服務器能夠處理對FLV文件的范圍請求&#xff08;Range Requests&#xff09;&#xff0c;因為流媒體通常不是一次性下載整個文件&…

CentOS 7.9 郵箱部署——Postfix+Dovecot詳細

PostfixDovecot 文章目錄 PostfixDovecot資源列表基礎環境一、部署DNS二、部署postfix和dovecot2.1、配置postfix2.2、配置dovecot2.3、創建郵件用戶 三、發送郵件測試3.1、windows安裝poxmail3.2、登錄郵箱3.3、發送接收郵件 四、搭建SSL認證加密4.1、生成私鑰4.2、生成公鑰4.…

正則工具類

目錄 1、 * 正則工具類 1.1、 * 提供驗證郵箱、手機號、電話號碼、身份證號碼、數字等方法 1.1.1、 * 驗證固定電話號碼 1.1.2、 * 驗證整數(正整數和負整數) 1.1.3、 * 驗證整數和浮點數(正負整數和正負浮點數)

貪心算法4(c++)

過河的最短時間 題目描述 輸入 在漆黑的夜里&#xff0c;N位旅行者來到了一座狹窄而且沒有護欄的橋邊。如果不借助手電筒的話&#xff0c;大家是無論如何也不敢過橋去的。不幸的是&#xff0c;N個人一共只帶了一只手電筒&#xff0c;而橋窄得只夠讓兩個人同時過&#xff0c;如果…

YOLOv8_pose預測流程-原理解析[關鍵點檢測理論篇]

YOLOv8_seg的網絡結構圖在博客YOLOv8網絡結構介紹_CSDN博客已經更新了,由網絡結構圖可以看到相對于目標檢測網絡,實例分割網絡只是在Head層不相同,如下圖所示,在每個特征層中增加了KeyPoint分支(淺綠色),通過兩個卷積組和一個Conv卷積得到得到通道數為51的特征圖,51表示…

window環境下QT5開發環境的搭建

1、安裝visual Stusio 15 生成工具2012 2、安裝Visual studio Enterprise 2017 3、Visual studio Enterprise 2017安裝完成之后&#xff0c; 修改&#xff1a;選擇桌面調試&#xff0c;如下&#xff1a; 4、打開QTcreator&#xff0c;選項中&#xff0c;配置編譯器&#xff…

摸魚大數據——Hive基礎理論知識——Hive環境準備

Hive環境準備 1、shell腳本執行方式 方式1: sh 腳本 注意: 需要進入腳本所在目錄,但腳本有沒有執行權限不影響執行 方式2: ./腳本 注意: 需要進入腳本所在目錄,且腳本必須有執行權限 方式3: /絕對路徑/腳本 注意: 不需要進入腳本所在目錄,但必須有執行…

線程池,日志

所要用到的知識點&#xff1a; 多線程的創建 生產消費模型&#xff0c; 線程鎖 條件變量 代碼&#xff1a; 線程池日志

基于STC12C5A60S2系列1T 8051單片機的TM1638鍵盤數碼管模塊的數碼管顯示與單片機連接的按鍵的按鍵值的功能

基于STC12C5A60S2系列1T 8051單片機的TM1638鍵盤數碼管模塊的數碼管顯示與單片機連接的按鍵的按鍵值應用 STC12C5A60S2系列1T 8051單片機管腳圖STC12C5A60S2系列1T 8051單片機I/O口各種不同工作模式及配置STC12C5A60S2系列1T 8051單片機I/O口各種不同工作模式介紹TM1638鍵盤數碼…

C++面向對象程序設計 - 輸入和輸出

程序的輸入指的是文件將數據傳送給程序&#xff0c;程序的輸出指的是從程序將數據傳送輸出文件。 C的輸入和和輸出包括以下三個方面&#xff1a; 對系統指定的標準設備的輸入和輸出&#xff0c;即從鍵盤輸入數據&#xff0c;輸出到顯示器屏幕。以外存磁盤&#xff08;或光盤、…

初探 Spring Boot Starter Security:構建更安全的Spring Boot應用

引言 Spring Boot 作為 Java 生態系統下的熱門框架&#xff0c;以其簡潔和易上手著稱。而在構建 Web 應用程序時&#xff0c;安全性始終是開發者必須重視的一個方面。Spring Boot Starter Security 為開發者提供了一個簡單但功能強大的安全框架&#xff0c;使得實現身份驗證和…

從動態代理角度簡單理解Spring AOP

1. 概述 動態代理 是指在運行時&#xff0c;動態地創建目標類的代理對象&#xff0c;并對其中特定的方法進行攔截或增強的技術。這種技術主要用于在不修改目標類代碼的情況下&#xff0c;增強目標類的功能。 在Java中&#xff0c;動態代理主要基于Java的反射機制和接口來實現…

gdc2024:Raytracing in Snowdrop技術實現與性能優化策略

在今年的GDC&#xff08;游戲開發者大會&#xff09;的Advanced Graphics Summit上&#xff0c;關于Snowdrop引擎中光線追蹤技術的討論引起了廣泛關注。 一、光線追蹤全局照明的實現細節 屏幕空間追蹤&#xff1a; 屏幕空間追蹤從相機出發&#xff0c;對屏幕上的每個像素點生成…

DDL—表—數據類型—字符串類型相關語法

&#xff08;1&#xff09;表格可視化 普通字符串 類型大小描述CHAR0~255 bytes定長字符串&#xff0c;其表示即使你存儲一個字符&#xff0c;它也會占用你括號里個數的字符的空間&#xff0c;因為未占用的字符的其它空間會用空格進行補位。需要再后面跟一個參數&#xff1a;…

harmony 鴻蒙ArkUI動畫/交互事件開發常見問題(ArkTS)

ArkUI動畫/交互事件開發常見問題(ArkTS) 焦點事件onBlur/onFocus回調無法觸發(API 9) 問題現象 焦點事件onBlur/onFocus回調無法觸發 解決措施 焦點事件默認情況下需要外接鍵盤的Tab鍵&#xff0c;或方向鍵觸發&#xff0c;點擊觸發焦點事件需要添加焦點控制屬性focusOnTo…