執行緒間通訊 生產者消費者模型

2021-09-02 02:45:18 字數 3775 閱讀 5061

所謂的執行緒間通訊,其實就是多個執行緒再操作同乙個資源,但是操作的動作不同。當某個執行緒進入synchronized塊後,共享資料的狀態不一定滿足該執行緒的需要,需要其他執行緒改變共享資料的狀態後才能執行,而由於當時執行緒對共享資源時獨佔的,它必須解除對共享資源的鎖定的狀態,通知其他執行緒可以使用該共享資源。

執行緒之間的通訊其實可以通過很多方法來實現,比如wait()和notify()。也可以通過lock中的類似方法來實現執行緒中的通訊,這裡主要來講解下wait()和notify()。

wait():釋放占有的物件鎖,執行緒進入等待池,釋放cpu,而其他正在等待的執行緒即可搶占此鎖,獲得鎖的執行緒即可執行程式。而sleep()不同的是,執行緒呼叫此方法後,會休眠一段時間,休眠期間,會暫時釋放cpu,但並不釋放物件鎖。也就是說,在休眠期間,其他執行緒依然無法進入此**內部。休眠結束,執行緒重新獲得cpu,執行**。wait()和sleep()最大的不同在於wait()會釋放物件鎖,而sleep()不會!

notify(): 該方法會喚醒因為呼叫物件的wait()而等待的執行緒,其實就是對物件鎖的喚醒,從而使得wait()的執行緒可以有機會獲取物件鎖。呼叫notify()後,並不會立即釋放鎖,而是繼續執行當前**,直到synchronized中的**全部執行完畢,才會釋放物件鎖。jvm則會在等待的執行緒中排程乙個執行緒去獲得物件鎖,執行**。需要注意的是,wait()和notify()必須在synchronized**塊中呼叫

上面簡單的介紹了wait()和notify()方法。其實對於執行緒中通訊最好的例子就是多執行緒中生產者消費者模型,下面一起看看wait()和notify()在生產者和消費者模型中的使用。

首先採用單生產者和單消費者來實現+1,-1,+1,-1的功能。下面數字類,裡面包含加一減一的同步方法

package com.aiqinhai.threadcommunication;

/** * 數字容器(裡面包含同的加一,減一方法)

* @author aiqinhai

* */

public class numberholder catch (interruptedexception e)

} // 能執行到這裡說明已經被喚醒 , 並且number為0

number++;

system.out.println(number);

notify();

} public synchronized void decrease() catch (interruptedexception e)

} // 能執行到這裡說明已經被喚醒 並且number不為0

number--;

system.out.println(number);

notify();

}}

加一線程

package com.aiqinhai.threadcommunication;

/** * 加一線程

* @author aiqinhai

*/public class increasethread extends thread

@override

public void run() catch (interruptedexception e)

numberholder.increase();

} }}

減一線程

package com.aiqinhai.threadcommunication;

/** * 減一線程

* @author aiqinhai

*/public class decreasethread extends thread

@override

public void run() catch (interruptedexception e)

numberholder.decrease();

} }}

測試主線程

package com.aiqinhai.threadcommunication;

/** * 測試單生產者,但消費者。實現+1,-1,+1,-1

* * @author aiqinhai

* */

public class numbertest

}

測試結果

上面的測試類中,我們只啟動了乙個生產者和乙個消費者。如果啟動兩個生產者兩個消費者結果又是怎樣呢?

修改測試類如下

package com.aiqinhai.threadcommunication;

/** * 測試多生產者,多消費者。實現+1,-1,+1,-1

* * @author aiqinhai

* */

public class numbertest

}

測試結果如下

上面的測試結果顯然不符合預期目標。

因為執行緒在wait()的時候,接收到其他執行緒的通知,即往下執行,不再進行判斷。兩個執行緒的情況下,喚醒的肯定是另乙個執行緒;但是在多個執行緒的情況下,執行結果就會混亂無序。比如,乙個可能的情況是,乙個增加執行緒執行的時候,其他三個執行緒都在wait,這時候第乙個執行緒呼叫了notify()方法,其他執行緒都將被喚醒,然後執行各自的增加或減少方法。

解決的方法就是:在被喚醒之後仍然進行條件判斷,去檢查要改的數字是否滿足條件,如果不滿足條件就繼續睡眠。把兩個方法中的if改為while即可。同時將notify()修改為notifyall()。具體**如下

package com.aiqinhai.threadcommunication;

/** * 數字容器(裡面包含同的加一,減一方法)

* 這是多生產者多消費者中修改之後的類

* @author aiqinhai

* */

public class numberholder catch (interruptedexception e)

} // 能執行到這裡說明已經被喚醒, 並且number為0

number++;

system.out.println(number);

notifyall();

} public synchronized void decrease() catch (interruptedexception e)

} // 能執行到這裡說明已經被喚醒, 並且number不為0

number--;

system.out.println(number);

notifyall();

}}

測試結果如下

上面 的修改的方法有點問題,我把之前的notify改為了notifyall避免出現死鎖的情況,但是這種做法也是又弊端的。如果被喚醒參與競爭的執行緒很多而不是兩個,而且最終也只有乙個執行緒能夠獲得物件鎖。顯然這種競爭是很浪費,所以後面jdk中出現了condition。他能根據條件進行喚醒,對於這塊我們後續再來深入。

參考文獻

生產者消費者執行緒間通訊模型

include pch.h include include include include include c stl所有的容器都不是執行緒安全 using namespace std if 0 unique lock condition variable 1.lock guard和unique l...

執行緒間通訊 生產者消費者實踐

簡單生產者消費者問題 現有兩個執行緒a b,對乙個初始值未零的數加減一的操作,不能為負數,二者必須要實現輪流操作 操作十次 就在前不久自己也遇到了乙個這樣的問題,但是沒有考慮到的是執行緒的通訊,利用迴圈完成的輪流執行,唉 慘,不多說了重新來!再一次 高內聚低耦合前提下實現執行緒操作資源類!資源類 c...

執行緒 執行緒間通訊(生產者消費者模式)

注意 1.執行緒間的通訊,共享的資料一定要有同步 塊synchronized 2.一定要有wait和notify,而且二者一定是成對出現 3.生產者和消費者的執行緒實現一定是在while true 裡面public class basket public void setempty boolean ...