生产者消费者案例
目錄
- 前言
- 一、案例描述
- 二、創(chuàng)建快遞柜
- 三、創(chuàng)建生產(chǎn)者類
- 四、創(chuàng)建消費(fèi)者類
- 五、測(cè)試類
- 總結(jié)
前言
生產(chǎn)者消費(fèi)者模式屬于一種經(jīng)典的多線程協(xié)作的模式,弄清生產(chǎn)者消費(fèi)者問題能夠讓我們對(duì)于多線程編程有更深刻的理解,下面,為大家分享一個(gè)生產(chǎn)者消費(fèi)者的案例。
一、案例描述
這里以快遞為例,假設(shè)有一個(gè)快遞柜,用來存快遞,然后有快遞員和取件人,快遞員往快遞柜里存快遞,取件人從快遞柜中取走快遞。快遞員作為生產(chǎn)者,取件人作為消費(fèi)者,當(dāng)兩者在一個(gè)時(shí)間段同時(shí)進(jìn)行多次自己的操作時(shí),很明顯這就是多線程編程的生產(chǎn)者消費(fèi)者實(shí)例了。在這里,我們希望快遞員(生產(chǎn)者)存入一個(gè)快遞,取件人(消費(fèi)者)就拿走一個(gè)快遞,如果快遞還沒有被取走,那么生產(chǎn)者應(yīng)該等待,而如果快遞柜里沒有快遞,則消費(fèi)者應(yīng)該等待。
首先來明確一下,這個(gè)案例我們需要準(zhǔn)備:
(1) 創(chuàng)建快遞柜對(duì)象作為共享數(shù)據(jù)區(qū)域
(2) 創(chuàng)建生產(chǎn)者,把快遞柜對(duì)象作為參數(shù)傳遞至構(gòu)造方法,因?yàn)樯a(chǎn)者需要完成存快遞的操作
(3)創(chuàng)建消費(fèi)者,把快遞柜作為對(duì)象傳遞至構(gòu)造方法,因?yàn)橄M(fèi)者需要完成取快遞的操作
(4)創(chuàng)建兩個(gè)線程,將生產(chǎn)者和消費(fèi)者對(duì)象分別作為參數(shù)傳遞至線程的構(gòu)造方法,然后啟動(dòng)線程
下面是具體實(shí)現(xiàn):
二、創(chuàng)建快遞柜
代碼如下:
public class Box {//定義成員變量表示第幾個(gè)快遞(快遞序號(hào))private int express;//定義一個(gè)成員變量用于表示快遞柜的狀態(tài)private boolean flag = false;//存快遞public synchronized void put(int express) {//如果有快遞,那么快遞員應(yīng)該等待取件人來取快遞if (flag) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}//如果沒有快遞,那么快遞員就存入快遞this.express = express;System.out.println("快遞員將第" + this.express + "個(gè)快遞存入了快遞柜");//別忘了存完修改快遞柜的狀態(tài)flag = true;//修改完快遞柜狀態(tài)后,喚醒其他在等待的線程notifyAll();}//取快遞public synchronized void get() {//如果有快遞,那么取件人就取走快遞if (flag) {System.out.println("取件人取出了第" + this.express + "個(gè)快遞");flag = false;notifyAll();} else {//沒有快遞,那么取件人就等待try {wait();} catch (InterruptedException e) {e.printStackTrace();}}} }說明:
如之前的分析,我們創(chuàng)建了一個(gè)Box類當(dāng)做快遞柜,除了表示快遞序號(hào)的成員變量以外和對(duì)應(yīng)的存快遞、取快遞方法外,還包括一個(gè)用來標(biāo)記快遞柜狀態(tài)的變量,因?yàn)榫€程執(zhí)行時(shí)需要這個(gè)標(biāo)記來判斷是該執(zhí)行還是等待。存快遞和取快遞的方法都加上了sychronized變成了同步方法,因?yàn)橛糜诘却膚ait()方法和喚醒的notifyAll()方法要在sychronized塊中使用,否則會(huì)拋出 IllegalMonitorStateException異常而無法執(zhí)行。
三、創(chuàng)建生產(chǎn)者類
代碼如下:
public class Producer implements Runnable{private Box b;public Producer(Box b){this.b = b;}@Overridepublic void run() {for(int i = 1 ;i<11;i++){b.put(i);}} }說明:
快遞員當(dāng)做生產(chǎn)者類,它實(shí)現(xiàn)了Runnable接口,重寫了run()方法,并且有一個(gè)Box類型的成員變量,和一個(gè)以這個(gè)成員變量為參數(shù)的構(gòu)造方法,因?yàn)樵谶@個(gè)類中要調(diào)用存快遞的操作。在這里,run()方法里一共存入了10次快遞。
四、創(chuàng)建消費(fèi)者類
代碼如下:
public class Customer implements Runnable{private Box b ;public Customer(Box b){this.b = b;}@Overridepublic void run() {while(true){b.get();}} }說明:
同生產(chǎn)者類一樣,消費(fèi)者(取件人)類也實(shí)現(xiàn)了Runnable接口,重寫了run()方法,同樣有一個(gè)Box類型的成員變量,和一個(gè)以這個(gè)成員變量為參數(shù)的構(gòu)造方法,因?yàn)檫@個(gè)類里會(huì)調(diào)用取快遞的操作。由于能取快遞的次數(shù)是由生產(chǎn)者(快遞員)存入多少快遞決定的,所以這里我們直接用while循環(huán)就好了。
五、測(cè)試類
在測(cè)試類中,我們分別創(chuàng)建快遞柜、生產(chǎn)者和消費(fèi)者的對(duì)象,將快遞柜對(duì)象作為參數(shù)分別傳入生產(chǎn)者和消費(fèi)者創(chuàng)建時(shí)的構(gòu)造方法。然后創(chuàng)建兩個(gè)線程,分別將生產(chǎn)者和消費(fèi)者對(duì)象作為構(gòu)造方法的參數(shù)傳遞,最后啟動(dòng)線程,觀察結(jié)果。
代碼如下:
public class BoxDemo {public static void main(String[] args) {//創(chuàng)建快遞柜對(duì)象Box box = new Box();//創(chuàng)建生產(chǎn)者和消費(fèi)者對(duì)象Producer p = new Producer(box);Customer c = new Customer(box);//創(chuàng)建兩個(gè)線程Thread t1 = new Thread(p,"生產(chǎn)者線程");Thread t2 = new Thread(c,"消費(fèi)者線程");//啟動(dòng)線程t1.start();t2.start();} }執(zhí)行結(jié)果:
可以看到,快遞員和取件人有序地完成了10個(gè)快遞的存和取。
總結(jié)
以上就是一個(gè)簡單的生產(chǎn)者和消費(fèi)者的案例,從這里面我們可以看出,這種模式除了生產(chǎn)者、消費(fèi)者以外,還有一個(gè)很重要的中介的數(shù)據(jù)緩存區(qū),也就是案例中的快遞柜,生產(chǎn)者往里面“丟”,消費(fèi)者從里面“拿”,這樣三者才構(gòu)成了完整的生產(chǎn)者消費(fèi)者模式。
總結(jié)
- 上一篇: nn.Module、nn.Sequent
- 下一篇: 指北针软件