第三章執行緒間的通訊第一節

2021-08-28 03:27:47 字數 4048 閱讀 1476

3.1.1不使用等待/通知機制實現執行緒間通訊

3.1.2什麼是等待/通知機制

兩個執行緒完全是主動式地讀取乙個變數,在花費讀取時間的基礎上,讀到的資料並不確定是否是想要的,因此需要「等待通知」機制

3.1.3等待/通知機制的實現

1wait()方法:作用是使當前執行**的執行緒進行等待,該方法是object類的方法,用來將當前執行緒置入「預執行佇列」中,並在wait()所在**行處停止執行,直到接到通知或被中斷為止。在呼叫wait()方法之前,執行緒必須獲得該物件的物件級別鎖,即只能在同步方法或同步**塊中呼叫wait()方法。在執行wait()方法後,當前執行緒釋放鎖。在從wait()返回前,執行緒與其他執行緒競爭重新獲得鎖。如果在呼叫wait()時沒有持有適當的鎖,則會丟擲異常,而不是try/catch捕獲異常,因為該異常是runtimeexception的乙個子類

2notify()方法:也要在同步方法或同步**塊中呼叫,如果呼叫notify()時沒有持有適當的鎖,也會丟擲和wait()一樣的異常。該方法用來通知那些可能等待該物件的物件鎖的其他執行緒,如果有多個執行緒等待,則由執行緒規劃器隨機挑選出其中乙個呈wait狀態的執行緒,對其發出通知notify,並使它等待獲取該物件的物件鎖。

在執行notify方法後,當前執行緒不會馬上釋放該物件鎖,呈wait狀態的執行緒也並不能馬上獲取該物件鎖,要等到執行notify方法的執行緒將程式執行完,也就是退出synchronized**塊後,當前執行緒才會釋放鎖,而呈wait狀態所在的執行緒才可以獲取該物件鎖

總結:兩個方法都要獲得該物件的物件級別鎖才能呼叫,wait()執行後,當前執行緒立即釋放該物件的物件鎖;notify()執行後,當前執行緒要等到同步**塊執行完畢後才能釋放鎖,並喚醒其他執行緒

這兩個方法是object型別的方法,在進行等待和通知時都是由鎖物件自己來呼叫方法的

wait()方法可以使呼叫該方法的執行緒釋放共享資源的鎖,然後從執行狀態退出,進入等待佇列,直到被再次喚醒

notify()方法可以隨機喚醒等待佇列中等待同一共享資源的乙個執行緒,使執行緒從等待狀態進入執行狀態

notifyall()方法可以是所有正在等待佇列中等待同一共享資源的「全部執行緒」從等待狀態退出,進入執行狀態

執行緒的四種狀態

1)新建立乙個新的執行緒物件後,再呼叫它的start()方法,系統會為此執行緒分配cpu資源,使其處於runnable(可執行)狀態,這是 乙個準備執行的狀態。如果執行緒搶占到了cpu資源,此執行緒就處於running(執行)狀態

2)runnable,running可以互相切換,因為有可能執行緒一段時間後,有其他高優先順序的執行緒搶占到了cpu資源

執行緒進入runnable狀態的五種情況:

呼叫sleep()方法後經過的時間超過了指定的休眠時間

執行緒呼叫的阻塞io已經返回,阻塞方法執行完畢

執行緒成功地獲得了試圖同步的監視器

執行緒正在等待某個通知,其他執行緒發出了通知

處於掛起狀態的執行緒呼叫了resume恢復方法

3)blocked阻塞狀態:如果遇到了乙個io操作,而cpu此時處於空閒狀態,可能會轉而把cpu分給其他執行緒,這是稱為暫停狀態

出現阻塞狀態的5種情況:

執行緒呼叫sleep方法,主動放棄占用處理資源

執行緒呼叫了阻塞式io方法,在該方法返回前,該執行緒被阻塞

執行緒試圖獲取乙個同步監視器,但該監視器正被其他執行緒所持有

執行緒等待某個通知

程式呼叫了suspend方法將該執行緒掛起,此方法容易出現死鎖,避免使用

4)run方法執行結束後進入銷毀階段,整個執行緒執行完畢

每個鎖物件都有兩個兩個佇列:乙個是就緒佇列,乙個是阻塞佇列

3.1.4方法wait鎖釋放與notify鎖不釋放

當方法wait被執行後,鎖被自動釋放,但執行完notify方法,鎖不自動釋放,要等到執行緒執行完同步**塊後釋放鎖

package three.fourth.test;

import three.fourth.extthread.threada;

public class test2 catch (interruptedexception e)

a.interrupt();

}}

執行完同步**塊就會釋放物件的鎖

在執行同步**塊的過程中,遇到異常而導致執行緒終止,鎖也會被釋放

在執行同步**塊的過程中,執行力鎖所屬物件的wait方法,這個執行緒會釋放物件鎖,而次執行緒物件會進入執行緒等待池中,等待被喚醒

3.1.6只通知乙個執行緒

呼叫方法notify一次只通知乙個執行緒進行喚醒

當多次呼叫notify方法時,會隨機將等待wait狀態的執行緒進行喚醒

3.1.7喚醒所有執行緒

當notify方法的呼叫次數小於執行緒物件的數量,會出現有部分執行緒物件物件無法被喚醒的情況,可以使用notifyall方法喚醒全部執行緒

3.1.8方法wait(long)的使用

該方法的功能是等待某一時間內是否有執行緒對鎖進行喚醒,如果超過這個時間則自動喚醒

3.1.9通知過早

如果通知過早,會打斷程式的正常邏輯

為了防止通知過早的情況,可以設定乙個布林型別的標誌位

3.1.10等待wait的條件發生變化

此處體現出if和while的區別,if只能判斷一次,而while可以迴圈判斷,可以避免覆蓋

3.1.11生產者/消費者模式實現

一生產與一消費:操作值

建立生產者類:

package productandclient;

public class p

public void setvalue() catch (interruptedexception e)

}string value = system.currenttimemillis()+"_"+system.nanotime();

system.out.println("set的值是"+value);

valueobject.value = value;

lock.notify();

} }}

建立消費者類:

package productandclient;

public class c

public void getvalue() catch (interruptedexception e)

} system.out.println("get的值為"+valueobject.value);

valueobject.value = "";

lock.notify();

} }}

建立生產者執行緒和消費者執行緒:

package productandclient;

public class threadp extends thread

public void run() }}

package productandclient;

public class threadc extends thread

public void run()

}}

執行測試類:

package productandclient;

public class run

}

執行結果如圖所示:

可以看出set和get方法是交替執行的,但是誰先執行是隨機的,看誰先搶到執行緒的執行權

多生產與多消費:操作值-假死

「假死」現象就是執行緒進入了waiting等待狀態,如果全部執行緒都進入了waiting狀態,則程式就不再執行任何業務功能了,整個專案呈停止狀態

由於在這種模式下生產者可能會喚醒生產者,消費者可能會喚醒消費者,這樣積累下去,最後導致所有執行緒都呈waiting狀態,程式最終呈現假死狀態

解決方法:將notify方法換成notifyall方法,不光喚醒同類執行緒,也喚醒異類執行緒

解決wait條件改變:if換成while

第三章 第一節

記憶體中字的儲存 在0位址處開始存放4e20h這個字 4eh是高位資料,20h是低位資料 intel的cpu是小尾 小端 順序,也就是說,低位資料存放在低位址的記憶體單元中,高位資料存放在高位址的記憶體單元中。針對上圖,提出如下問題 結論 任何兩個位址連續的記憶體單元,n號單元和n 1號單元,可以將...

第三章 第一節 條件判斷語句

本節主要講解python中的條件判斷語句,條件判斷語句在開發中是經常使用到的,通常我們使用條件判斷語句來執行不同的 段。python主要的條件語句有 if語句 if else語句 if elif語句。再學習完這三種條件判斷語句時,我們還會實現switch語句。零 if語句 if語句的語法是這樣的 i...

python小白之路 第三章函式第一節

變數的作用域 當前起作用,可用的範圍區域,也就是變數的有效範圍 在定義或引用時,global 關鍵字 在函式內外都可以使用的變數 在函式內部可以使用的變數,在函式內定義後,外部不能直接用,會報錯 在多層函式裡時,nonlocal 關鍵字來呼叫上層的區域性變數 在函式外定義的變數,在函式內可以使用或操...