生產(chǎn)者消費(fèi)者模式是并發(fā)、多線(xiàn)程編程中經(jīng)典的設(shè)計(jì)模式,生產(chǎn)者和消費(fèi)者通過(guò)分離的執(zhí)行工作解耦,簡(jiǎn)化了開(kāi)發(fā)模式,生產(chǎn)者和消費(fèi)者可以以不同的速度生產(chǎn)和消費(fèi)數(shù)據(jù)。這篇文章我們來(lái)看看什么是生產(chǎn)者消費(fèi)者模式,這個(gè)問(wèn)題也是多線(xiàn)程面試題中經(jīng)常被提及的。如何使用阻塞隊(duì)列(Blocking Queue)解決生產(chǎn)者消費(fèi)者模式,以及使用生產(chǎn)者消費(fèi)者模式的好處。
真實(shí)世界中的生產(chǎn)者消費(fèi)者模式
生產(chǎn)者和消費(fèi)者模式在生活當(dāng)中隨處可見(jiàn),它描述的是協(xié)調(diào)與協(xié)作的關(guān)系。比如一個(gè)人正在準(zhǔn)備食物(生產(chǎn)者),而另一個(gè)人正在吃(消費(fèi)者),他們使用一個(gè)共用的桌子用于放置盤(pán)子和取走盤(pán)子,生產(chǎn)者準(zhǔn)備食物,如果桌子上已經(jīng)滿(mǎn)了就等待,消費(fèi)者(那個(gè)吃的)等待如果桌子空了的話(huà)。這里桌子就是一個(gè)共享的對(duì)象。在Java Executor框架自身實(shí)現(xiàn)了生產(chǎn)者消費(fèi)者模式它們分別負(fù)責(zé)添加和執(zhí)行任務(wù)。
生產(chǎn)者消費(fèi)者模式的好處
它的確是一種實(shí)用的設(shè)計(jì)模式,常用于編寫(xiě)多線(xiàn)程或并發(fā)代碼。下面是它的一些優(yōu)點(diǎn):
多線(xiàn)程中的生產(chǎn)者消費(fèi)者問(wèn)題
生產(chǎn)者消費(fèi)者問(wèn)題是一個(gè)流行的面試題,面試官會(huì)要求你實(shí)現(xiàn)生產(chǎn)者消費(fèi)者設(shè)計(jì)模式,以至于能讓生產(chǎn)者應(yīng)等待如果隊(duì)列或籃子滿(mǎn)了的話(huà),消費(fèi)者等待如果隊(duì)列或者籃子是空的。這個(gè)問(wèn)題可以用不同的方式來(lái)現(xiàn)實(shí),經(jīng)典的方法是使用wait和notify方法在生產(chǎn)者和消費(fèi)者線(xiàn)程中合作,在隊(duì)列滿(mǎn)了或者隊(duì)列是空的條件下阻塞,Java5的阻塞隊(duì)列(BlockingQueue)數(shù)據(jù)結(jié)構(gòu)更簡(jiǎn)單,因?yàn)樗[含的提供了這些控制,現(xiàn)在你不需要使用wait和nofity在生產(chǎn)者和消費(fèi)者之間通信了,阻塞隊(duì)列的put()方法將阻塞如果隊(duì)列滿(mǎn)了,隊(duì)列take()方法將阻塞如果隊(duì)列是空的。在下部分我們可以看到代碼例子。
使用阻塞隊(duì)列實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模式
阻塞隊(duì)列實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模式超級(jí)簡(jiǎn)單,它提供開(kāi)箱即用支持阻塞的方法put()和take(),開(kāi)發(fā)者不需要寫(xiě)困惑的wait-nofity代碼去實(shí)現(xiàn)通信。BlockingQueue 一個(gè)接口,Java5提供了不同的現(xiàn)實(shí),如ArrayBlockingQueue和LinkedBlockingQueue,兩者都是先進(jìn)先出(FIFO)順序。而ArrayLinkedQueue是自然有界的,LinkedBlockingQueue可選的邊界。下面這是一個(gè)完整的生產(chǎn)者消費(fèi)者代碼例子,對(duì)比傳統(tǒng)的wait、nofity代碼,它更易于理解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.logging.Level; import java.util.logging.Logger; public class ProducerConsumerPattern { public static void main(String args[]){ //Creating shared object BlockingQueue sharedQueue = new LinkedBlockingQueue(); //Creating Producer and Consumer Thread Thread prodThread = new Thread( new Producer(sharedQueue)); Thread consThread = new Thread( new Consumer(sharedQueue)); //Starting producer and Consumer thread prodThread.start(); consThread.start(); } } //Producer Class in java class Producer implements Runnable { private final BlockingQueue sharedQueue; public Producer(BlockingQueue sharedQueue) { this .sharedQueue = sharedQueue; } @Override public void run() { for ( int i= 0 ; i< 10 ; i++){ try { System.out.println( "Produced: " + i); sharedQueue.put(i); } catch (InterruptedException ex) { Logger.getLogger(Producer. class .getName()).log(Level.SEVERE, null , ex); } } } } //Consumer Class in Java class Consumer implements Runnable{ private final BlockingQueue sharedQueue; public Consumer (BlockingQueue sharedQueue) { this .sharedQueue = sharedQueue; } @Override public void run() { while ( true ){ try { System.out.println( "Consumed: " + sharedQueue.take()); } catch (InterruptedException ex) { Logger.getLogger(Consumer. class .getName()).log(Level.SEVERE, null , ex); } } } } Output: Produced: 0 Produced: 1 Consumed: 0 Produced: 2 Consumed: 1 Produced: 3 Consumed: 2 Produced: 4 Consumed: 3 Produced: 5 Consumed: 4 Produced: 6 Consumed: 5 Produced: 7 Consumed: 6 Produced: 8 Consumed: 7 Produced: 9 Consumed: 8 Consumed: 9 |
你可以看到生產(chǎn)者線(xiàn)程生產(chǎn)數(shù)和消費(fèi)者線(xiàn)程消費(fèi)它以FIFO的順序,因?yàn)樽枞?duì)列只允許元素以FIFO的方式來(lái)訪(fǎng)問(wèn)。以上就是使用阻塞隊(duì)列解決生產(chǎn)者消費(fèi)者問(wèn)題的全部,我確信它比wait/notify更簡(jiǎn)單
聯(lián)系客服