生產者消費者問題
現在我們要寫一個程式,來模擬做饅頭和吃饅頭,一個不斷的往籃子里扔做熟的饅頭,一個不斷的在那吃,模型如下:

現在要求是,寫一個程式來模擬這個程序,面向物件,怎么寫呢?
我們要寫考慮里面有哪些類呢?我們分析名字就行了
首先我們先把主要的類和main方法寫一下:
public class ProducerConsumer {
public static void main(String[] args) {
}
}
然后寫一下饅頭類:
class WoTou {
int id;
WoTou(int id) {
this.id = id;
}
public String toString() {
return "WoTou : " + id;
}
}
給他一個id,可以更好地看清哪個饅頭做出來了,那個饅頭被吃了,
接下來是籃子類,我們想一下,我們先做的饅頭是放在底層的是吧,別人要吃都是從上面開始拿的,先進后出,堆疊,
class SyncStack {
int index = 0;//裝到第幾個了
WoTou[] arrWT = new WoTou[6];//這個籃子能裝6個饅頭
//裝饅頭的方法
public synchronized void push(WoTou wt) {
arrWT[index] = wt;
index ++;//放了饅頭,肯定要占一個位置的
}
要是上面程式不加鎖會怎樣呢?
當你在一個位置裝了饅頭之后,有另外一個執行緒,放饅頭的人也好,吃饅頭的人也好,都是一個執行緒,你往里扔饅頭的時候,有一個人已經扔進去了,可是他被打斷了,index還沒來得及往上加,下一個人又往里扔,會產生什么問題啊,會把原來的饅頭給覆寫掉,index還是0,怎么解決呢?加個鎖就好了!
還有一個問題就是,如果籃子滿了怎么辦呢?我們是不是得休息一下啊,所以要進行執行緒控制,用wait方法:
public synchronized void push(WoTou wt) {
while(index == arrWT.length) {
try {
this.wait();//指的是SyncStack,讓生產者休息
} catch (InterruptedException e) {
e.printStackTrace();
}
}
arrWT[index] = wt;
index ++;
}
wait的解釋是,當前的,正在我這個物件訪問的執行緒,
一個執行緒訪問push()方法的時候,他已經拿到這個物件的鎖了,拿到物件這個鎖的執行緒,在執行的程序之中,他遇見一個事件必須阻塞住,必須停止,什么事件,已經滿了,你不能往里填了,必須等清潔工把它清走,才可以繼續,沒清走之前,你只能等著,
這里我們說一下wait和sleep的區別:
上面代碼是不行的,為什么不行呢,因為這個東西全等在那了,第一個人生產滿了,他就在那等住了,等住了之后,另外一個人就算消費空了,籃子里邊沒有饅頭了,可是他依然運行不了,為什么呀?因為
沒有人叫醒他,
wait時別的執行緒可以訪問鎖定物件,呼叫wait方法的時候必須鎖定該物件,
sleep時別的執行緒也不可以訪問鎖定物件
wait和sleep不一樣,sleep過一段時間他會自己醒過來,可是wait不行,wait一旦wait過去,對不起,死過去了,并且,wait一旦wait過去了,這個物件的鎖不在歸我所有,只有醒過來的時候才會再去找這把鎖,這是wait和sleep之間巨大的區別,wait的時候,鎖不在歸我所有,sleep的時候,睡著了也得抱著那把鎖,
wait完之后我們要干嘛呢?我們要做一件事,就是叫消費者趕快消費是吧,這樣籃子才會有空位置,這時我們就要用notify方法了,wait方法和notify方法是一一對應的,所以我們就得在代碼中加上this.notify(),當有多個執行緒就得用this.notiyAll()
notify的含義:叫醒一個正在wait在我這個物件上的執行緒,this.notify(),誰現在正在我這個物件等待著,我就叫醒誰,讓它繼續執行
那我們既然有放饅頭的方法,肯定也有拿饅頭的方法是吧:
public synchronized WoTou pop() {
index--;//拿了饅頭,便把位置數-1
return arrWT[index];
}
pop方法也一樣,當籃子里的饅頭空了之后,就必須等待著
public synchronized WoTou pop() {
while(index == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
index--;
return arrWT[index];
}
接下來我們還得有生產者這個類吧,不然饅頭哪里來?他是一個執行緒,因為好多人在一起做饅頭,
class Producer implements Runnable {
SyncStack ss = null;//生產者得知道往哪個框生產吧,所以得參考一個物件,
Producer(SyncStack ss) {
this.ss = ss;
}
有了執行緒,就得有啟動執行緒的方法
public void run() {
for(int i=0; i<20; i++) {
WoTou wt = new WoTou(i);
ss.push(wt);
System.out.println("生產了:" + wt);
try {
Thread.sleep((int)(Math.random() * 1000));
//為了方便觀察,每生產一個,便睡眠一會
//Math.random()是一個double型別,這里強制轉換為int型別
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
有生產者的類,自然也就有消費者的類:
class Consumer implements Runnable {
SyncStack ss = null;
Consumer(SyncStack ss) {
this.ss = ss;
}
}
消費者這個執行緒自然也有啟動的方法:
public void run() {
for(int i=0; i<20; i++) {
WoTou wt = ss.pop();
System.out.println("消費了: " + wt);
try {
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
接下來我們就得模擬一下執行緒了,首先得造一個框出來裝饅頭吧,
public class ProducerConsumer {
public static void main(String[] args) {
SyncStack ss = new SyncStack();
Producer p = new Producer(ss);
Consumer c = new Consumer(ss);
new Thread(p).start();
new Thread(c).start();
}
}
這里只模擬了一個生產者和一個消費者,
下面來看看完整代碼:
package Thread;
public class ProducerConsumer {
public static void main(String[] args) {
SyncStack ss = new SyncStack();
Producer p = new Producer(ss);
Consumer c = new Consumer(ss);
new Thread(p).start();
new Thread(p).start();
new Thread(c).start();
new Thread(c).start();
}
}
class WoTou {
int id;
WoTou(int id) {
this.id = id;
}
public String toString() {
return "WoTou : " + id;
}
}
class SyncStack {
int index = 0;
WoTou[] arrWT = new WoTou[6];
public synchronized void push(WoTou wt) {
while(index == arrWT.length) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();
arrWT[index] = wt;
index ++;
}
public synchronized WoTou pop() {
while(index == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();
index--;
return arrWT[index];
}
}
class Producer implements Runnable {
SyncStack ss = null;
Producer(SyncStack ss) {
this.ss = ss;
}
public void run() {
for(int i=0; i<20; i++) {
WoTou wt = new WoTou(i);
ss.push(wt);
System.out.println("生產了:" + wt);
try {
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable {
SyncStack ss = null;
Consumer(SyncStack ss) {
this.ss = ss;
}
public void run() {
for(int i=0; i<20; i++) {
WoTou wt = ss.pop();
System.out.println("消費了: " + wt);
try {
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
多執行緒(三)也寫到這啦,這個章節比較簡單,就說了一下生產者和消費者的問題,也說了wait和sleep的區別,這個是重點!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/209120.html
標籤:其他
上一篇:在已經撰寫完整的Qt Creator專案中創建ros節點并發布、接收訊息(不用下載插件)
下一篇:MySQL資料庫學習2
