Java多執行緒/併發11、執行緒同步通訊:notify、wait
假設有兩個執行緒,一個執行緒負責列印5次”Hello”,一個執行緒負責列印5次”Word”。現在提出一個要求,要求兩個執行緒交替列印,也就是要求Hello和Word交替出現。
我們先實現兩個執行緒列印字元的功能。程式碼如下:
package JConcurrence.Study;
public class ExecuteDemo {
public static void main(String[] args) {
final SyncLockTest LockTest = new SyncLockTest();
/* 第一個執行緒說5次Hello */
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 5; i++) {
LockTest.Hello();
}
}
}).start();
/* 第二個執行緒說5次Word */
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 5; i++) {
LockTest.Word();
}
}
}).start();
}
}
/* 定義外部測試類SyncLockTest */
class SyncLockTest {
public void Hello() {
System.out.println(Thread.currentThread().getName() + "say:Hello");
}
public void Word() {
System.out.println(Thread.currentThread().getName() + "say:World");
}
}
執行結果:
Thread-0say:Hello
Thread-0say:Hello
Thread-0say:Hello
Thread-0say:Hello
Thread-0say:Hello
Thread-1say:World
Thread-1say:World
Thread-1say:World
Thread-1say:World
Thread-1say:World
可以看到結果並沒有交替出現。
這裡要強調兩點:
1、Hello()和Word()都沒有使用synchronized方法。因為每個方法中只有一條System.out.println語句,System.out屬於臨界資源,不會因為多個執行緒的競爭,而破化輸出。所以不會出現一個字串還沒有列印完時(如:只輸出前幾個字元的時侯),就被另一個執行緒搶奪的情況。所以System.out.println本身是執行緒安全的,不需要畫蛇添足的加上synchronized。
2、執行結果排得很整齊,感覺像是先執行完Thread-0,再執行Thread-1。千萬別被迷惑,那是因為計算機處理能力強大了。
如果在每個方法前加上sleep模擬耗時操作,就會看到,兩個程式不分先後的往外輸出。
public void Hello() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "say:Hello");
}
public void Word() {
try {
Thread.sleep(7);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "say:World");
}
這時結果很零亂:
Thread-1say:World
Thread-0say:Hello
Thread-1say:World
Thread-0say:Hello
Thread-1say:World
Thread-1say:World
Thread-0say:Hello
Thread-1say:World
Thread-0say:Hello
Thread-0say:Hello
不過以上並不是本文重點。
現在開始說重點,我們如何實現兩個執行緒交替列印呢?這裡用到了同步對像鎖的wait()和 notify()方法。
wait()、notify()是定義在Object類裡的方法,可以用來控制執行緒的狀態。這三個方法最終呼叫的都是jvm級的native方法。隨著jvm執行平臺的不同可能有些許差異。
1、如果物件呼叫了wait方法就會使持有該物件的執行緒把該物件的控制權交出去,然後處於等待狀態。
2、如果物件呼叫了notify方法就會通知某個正在等待這個物件的控制權的執行緒可以繼續執行。如果有多個等待的執行緒,那麼會依靠JVM排程選出一個執行緒執行。
3、另外還有一個notifyAll方法,會通知所有等待這個物件控制權的執行緒繼續執行。
現在改造SyncLockTest類:
1、首先讓兩個執行緒方法成為有著相同鎖的synchronized同步方法,即在方法前加上synchronized關鍵字
2、新增兩個Boolen型別的標記,用於判斷當前哪個方法執行,哪個方法阻塞。
3、當標記不滿足當前方法執行的條件時,使用wait()對執行當前方法的執行緒阻塞。
4、在執行完當前方法的功能後,更新兩個Boolen標記值,同時呼叫notify()喚醒執行另一個方法的執行緒。
修改後的類程式碼如下:
/* 定義外部測試類SyncLockTest */
class SyncLockTest {
/*
* 為了增加程式碼可讀性,這裡用了兩個Boolean變數作為正在執行方法的標記
* Hello_WillRun如果等於true,表明Hello()方法即將要獲得this鎖,否則就保持wait
* 因為兩個方法是交替執行,同一時間只有一個執行,所以兩個變數必須保持互反:Hello_WillRun=!Word_WillRun
* 預設將先執行的那個設為true,後執行的設為false
*/
Boolean Hello_WillRun = true;
Boolean Word_WillRun = false;
/*兩個方法擁有同樣的鎖:this*/
public synchronized void Hello() {
/* 如果Hello()不是接下來將要執行的狀態,即:!Hello_WillRun,那麼保持等待wait()
while用於防止執行緒假醒後,順序往下執行輸出功能,從而破壞交替輸出*/
while (!Hello_WillRun) {
try {
/* Hello()進行等待
* 呼叫wait()和notify()的物件必須和synchronized鎖物件一致,因此這裡用this*/
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/*執行Hello()核心功能*/
System.out.println(Thread.currentThread().getName() + "say:Hello");
/* Hello()執行完畢,設定下一步的標記狀態值 */
Hello_WillRun = false;
Word_WillRun = true;
/*喚醒另一個執行緒*/
this.notify();
}
/*兩個方法擁有同樣的鎖:this*/
public synchronized void Word() {
while (!Word_WillRun) {
try {
/* Word()進行等待
* 呼叫wait()和notify()的物件必須和synchronized鎖物件一致,因此這裡用this*/
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/*執行Word()核心功能*/
System.out.println(Thread.currentThread().getName() + "say:World");
/* Word()執行完畢,設定下一步的狀態值 */
Hello_WillRun = true;
Word_WillRun = false;
/*喚醒另一個執行緒*/
this.notify();
}
}
成功輸出結果:
Thread-0say:Hello
Thread-1say:World
Thread-0say:Hello
Thread-1say:World
Thread-0say:Hello
Thread-1say:World
Thread-0say:Hello
Thread-1say:World
Thread-0say:Hello
Thread-1say:World
相關文章
- 【Java】【多執行緒】兩個執行緒間的通訊、wait、notify、notifyAllJava執行緒AI
- Java多執行緒中的wait/notify通訊模式Java執行緒AI模式
- java多執行緒wait notify joinJava執行緒AI
- Java多執行緒學習(3)執行緒同步與執行緒通訊Java執行緒
- Java多執行緒-執行緒通訊Java執行緒
- 執行緒間的同步與通訊(2)——wait, notify, notifyAll執行緒AI
- java多執行緒 wait() notify()簡單使用Java執行緒AI
- Java多執行緒8:wait()和notify()/notifyAll()Java執行緒AI
- Java多執行緒的wait()和notify()例子Java執行緒AI
- Java多執行緒學習——執行緒通訊Java執行緒
- Java多執行緒 -- wait() 和 notify() 使用入門Java執行緒AI
- 多執行緒中的wait與notify執行緒AI
- 【java 多執行緒】多執行緒併發同步問題及解決方法Java執行緒
- Java併發程式設計之執行緒安全、執行緒通訊Java程式設計執行緒
- java多執行緒5:執行緒間的通訊Java執行緒
- JAVA多執行緒併發Java執行緒
- Java多執行緒—執行緒同步(單訊號量互斥)Java執行緒
- 多執行緒Demo學習(執行緒的同步,簡單的執行緒通訊)執行緒
- java多執行緒詳解(併發,並行,同步)Java執行緒並行
- Java多執行緒中wait 和 notify 方法理解Java執行緒AI
- 深入執行緒的wait()/notify()執行緒AI
- 多執行緒,執行緒類三種方式,執行緒排程,執行緒同步,死鎖,執行緒間的通訊,阻塞佇列,wait和sleep區別?執行緒佇列AI
- java多執行緒與併發 - 執行緒池詳解Java執行緒
- Java多執行緒/併發08、中斷執行緒 interrupt()Java執行緒
- 多執行緒與併發----Semaphere同步執行緒
- 多執行緒和多執行緒同步執行緒
- java 多執行緒 –同步Java執行緒
- java 多執行緒 --同步Java執行緒
- java 多執行緒 併發 面試Java執行緒面試
- Java 併發和多執行緒(一) Java併發性和多執行緒介紹[轉]Java執行緒
- JavaThread多執行緒同步、鎖、通訊Javathread執行緒
- 多執行緒併發篇——如何停止執行緒執行緒
- Java執行緒通訊Java執行緒
- java多執行緒間的通訊Java執行緒
- Java多執行緒——執行緒Java執行緒
- Java多執行緒/併發12、多執行緒訪問static變數Java執行緒變數
- Java併發和多執行緒4:使用通用同步工具CountDownLatch實現執行緒等待Java執行緒CountDownLatch
- 多執行緒之間通訊及執行緒池執行緒