一到多個執行緒充當生產者,生產元素。一到多個執行緒充當消費者,消費元素。在兩者之間插入乙個佇列充當緩衝區,建立起生產者和消費者的鬆散耦合。
正常情況下,即生產元素的速度和消費元素的速度差不多時,生產者和消費者其實是不需要去關注對方的。
生產者可以一直生產,因為佇列裡總是有空間。消費者可以一直消費,因為佇列裡總是有元素。即達到乙個動態的平衡。
但在特殊情況下,比如生產元素的速度很快,佇列裡沒有了空間,此時生產者必須自我「停工」,開始「休息」。
一旦消費者消費了元素之後,佇列裡才會有空間,生產者才可以重啟生產,所以,消費者在消費完元素後有義務去叫醒生產者復工。
更準確的說法應該是,只有在生產者「休息」時,消費者消費完元素後才需要去叫醒生產者。否則,其實可以不用叫醒,因為人家本來就沒休息。
反之,如果消費元素的速度很快,佇列裡沒有了元素,只需把上述情況顛倒過來即可。
但這樣的話就會引入乙個新的問題,就是要能夠準備的判斷出對方有沒有在休息,為此就必須定義乙個狀態變數,在自己即將開始休息時,自己設定下這個變數。
對方通過檢測這個變數,來決定是否進行叫醒操作。當自己被叫醒後,首先要做的就是清除一下這個變數,表明我已經醒來復工了。
這樣就需要多維護乙個變數和多了一部分判斷邏輯。可能有些人會覺得可以通過判斷佇列的「空」或「滿」(即佇列中的元素數目)來決定是否進行叫醒操作。
在高併發下,可能剛剛判斷佇列不為空,瞬間之後佇列可能已經變為空的了,這樣會導致邏輯出錯。執行緒可能永遠無法被叫醒。
因此,綜合所有,生產者每生產乙個元素後,都會通知消費者,「現在有元素的,你可以消費」。
同樣,消費者每消費乙個元素後,也會通知生產者,「現在有空間的,你可以生產」。
很明顯,這些通知很多時候(即對方沒有休息時)是沒有真正意義的,不過無所謂,只要忽略它們就行了。
首先要保證是正確的,「寧可錯殺一千,也不放過乙個」。
常見的生產者與消費者的實現有以下幾種:
下面以lock實現簡單生產者和消費者:
public static void main(string args)
/***
* 生產者**/
static class producer implements runnable
@override
public void run()
} catch (exception e)
}} /**
** 消費者**/
static class consumer implements runnable
@override
public void run()
} catch (exception e)
}} /**
** 生產佇列**/
static class queue
container[putindex] = ele;
println("生產元素:%d", ele);
putindex++;
if (putindex >= capacity)
count++;
println("通知消費者去消費。。。");
conscond.signalall();
} finally
}public object takeele() throws interruptedexception
object ele = container[takeindex];
println("消費元素:%d", ele);
takeindex++;
if (takeindex >= capacity)
count--;
println("通知生產者去生產。。。");
prodcond.signalall();
return ele;
} finally
}}
生產者消費者 生產者與消費者模式
一 什麼是生產者與消費者模式 其實生產者與消費者模式就是乙個多執行緒併發協作的模式,在這個模式中呢,一部分執行緒被用於去生產資料,另一部分執行緒去處理資料,於是便有了形象的生產者與消費者了。而為了更好的優化生產者與消費者的關係,便設立乙個緩衝區,也就相當於乙個資料倉儲,當生產者生產資料時鎖住倉庫,不...
生產者與消費者
include include include include include include define size of buffer 10 int buffer size of buffer 緩衝陣列 int in 0,out 0 採用迴圈佇列方式進行陣列的訪問 宣告訊號量 sem t ful...
生產者與消費者
include include include include include include handle mutex 互斥訊號量 handle full 滿緩衝區訊號量計數 handle empty 空緩衝區訊號量計數 void producer 生產者函式 void consumer 消費者函...