本篇blog寫(xiě)于15年4月疗认,之前在自己的搭建的Blog中收錄清笨,現(xiàn)遷移到簡(jiǎn)書(shū)器腋,希望可以讓更多的同學(xué)學(xué)習(xí)了解溪猿,本人會(huì)把之前原Blog中寫(xiě)的文章統(tǒng)統(tǒng)遷移到簡(jiǎn)書(shū),和大家共同學(xué)習(xí)探討纫塌,一起進(jìn)步诊县。下面進(jìn)入正題:
可能有很多同學(xué)不太了解生產(chǎn)者/消費(fèi)者模式,也可能有同學(xué)會(huì)說(shuō):"四人幫(Gang Of Four)的23中設(shè)計(jì)模式中根本就沒(méi)有生產(chǎn)者/消費(fèi)者模式啊措左。"依痊,你說(shuō)的也對(duì),GOF中是沒(méi)有生產(chǎn)者/消費(fèi)者模式怎披,GOF中的23種設(shè)計(jì)模式主要是基于OO的胸嘁,但模式可以是OO的,也可以不是OO的呀凉逛。所以性宏,消除你的疑慮吧,生產(chǎn)者/消費(fèi)者模式還是很強(qiáng)大的状飞,大多數(shù)時(shí)候毫胜,它是用來(lái)解決并發(fā)問(wèn)題的,后續(xù)诬辈,我會(huì)學(xué)習(xí)并總結(jié)一下JDK Concurrent包下的一些類酵使,但今天我們主要聊一聊這個(gè)有趣的模式吧。
生產(chǎn)者/消費(fèi)者模式簡(jiǎn)介
其實(shí)從字面意義上理解自晰,某個(gè)模塊負(fù)責(zé)生產(chǎn)數(shù)據(jù)凝化,而另一個(gè)模塊負(fù)責(zé)處理這些數(shù)據(jù)稍坯,那么產(chǎn)生數(shù)據(jù)的模塊酬荞,就稱為生產(chǎn)者而消費(fèi)數(shù)據(jù)的模塊就稱為消費(fèi)者嘍搓劫。但是生產(chǎn)者/消費(fèi)者模式還有一個(gè)重要的地方就是緩存區(qū),你可以把它理解為一個(gè)古時(shí)候的媒婆混巧,也就是現(xiàn)在的紅娘(舉例子而已枪向,并不是單身狗),生產(chǎn)者把產(chǎn)生的數(shù)據(jù)放入緩存區(qū)咧党,消費(fèi)者從緩存區(qū)取得數(shù)據(jù)進(jìn)行處理秘蛔。大部分人往往對(duì)圖像的理解和記憶是更加深刻的,如果你也是這樣那就看圖吧:(正所謂無(wú)圖言屌)
生產(chǎn)者/消費(fèi)者模式優(yōu)勢(shì)
-
解耦
生產(chǎn)者和消費(fèi)者之間沒(méi)有直接依賴傍衡,都依賴于緩存區(qū)深员,所以達(dá)到了解耦的目的。
-
并發(fā)
正因?yàn)樯a(chǎn)者和消費(fèi)者沒(méi)有直接耦合在一起蛙埂,所以生產(chǎn)者和消費(fèi)者是兩個(gè)獨(dú)立的并發(fā)主體倦畅,生產(chǎn)者把數(shù)據(jù)生產(chǎn)數(shù)來(lái),丟到緩存區(qū)绣的,就可以繼續(xù)生產(chǎn)數(shù)據(jù)了叠赐,消費(fèi)者亦是如此。
-
支持忙閑不均
緩沖區(qū)還有另一個(gè)好處屡江。如果制造數(shù)據(jù)的速度時(shí)快時(shí)慢芭概,緩沖區(qū)的好處就體現(xiàn)出來(lái)了。當(dāng)數(shù)據(jù)制造快的時(shí)候惩嘉,消費(fèi)者來(lái)不及處理罢洲,未處理的數(shù)據(jù)可以暫時(shí)存在緩沖區(qū)中。等生產(chǎn)者的制造速度慢下來(lái)文黎,消費(fèi)者再慢慢處理掉奏路。
既然我們已經(jīng)了解了生產(chǎn)者/消費(fèi)者模式,那么我們接下來(lái)就用代碼來(lái)實(shí)現(xiàn)一下吧:
public class ProducerConsumerPattern {
public static void main(String[] args) {
BlockingQueue blockingDeque = new LinkedBlockingQueue();
Thread threadProducer = new Thread(new Producer(blockingDeque));
Thread threadConsumer = new Thread(new Consumer(blockingDeque));
threadConsumer.start();
threadProducer.start();
}
static class Producer implements Runnable{
private final BlockingQueue blockingQueue;
public Producer(BlockingQueue blockingQueue) {
this.blockingQueue = blockingQueue;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
System.out.println("producer"+i);
blockingQueue.put(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class Consumer implements Runnable{
private final BlockingQueue blockingQueue;
public Consumer(BlockingQueue blockingQueue) {
this.blockingQueue = blockingQueue;
}
@Override
public void run() {
for (;;) {
try {
System.out.println("consumer"+blockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
上面的代碼是使用了concurrent包下的一個(gè)阻塞隊(duì)列BlockingQueue臊诊,相對(duì)來(lái)說(shuō)比較簡(jiǎn)單鸽粉,當(dāng)然你也可以使用wait()和notify()方法或者await()和signal()方法實(shí)現(xiàn),如果你有看過(guò)LinkeBlockingQueue的源碼抓艳,那么你會(huì)發(fā)現(xiàn)触机,LinkedBlockingQueue就使用了后者(為了簡(jiǎn)單明了,我使用了靜態(tài)內(nèi)部類玷或,生產(chǎn)環(huán)境當(dāng)中一般都是分開(kāi)的)儡首。
總結(jié)
筆者在閱讀了LinkedBlockingQueue的源碼并簡(jiǎn)單的看了下concurrent包下的類之后,發(fā)現(xiàn)著實(shí)有趣偏友,所以才會(huì)有文章開(kāi)頭講的計(jì)劃著在將來(lái)的一段時(shí)間里學(xué)習(xí)一下concurrent包蔬胯,如果你同樣有興趣,不妨花點(diǎn)時(shí)間看看吧位他,或許會(huì)有意想不到的收獲哦氛濒。
如果你想更加細(xì)致的了解生產(chǎn)者/消費(fèi)者模式产场,建議參考編程隨想的生產(chǎn)者/消費(fèi)者模式概述。