java生產(chǎn)者與消費者模式
生產(chǎn)者和消費者模式是我們在學習多線程中很經(jīng)典的一個模式欠动,它主要分為生產(chǎn)者和消費者,分別是兩個線程.
一:生產(chǎn)者和消費者模式簡介
二:生產(chǎn)者和消費者模式的實現(xiàn)
一個自助餐里有一個做飯的廚師和來吃飯的人在扰,消費者消費食物,這里就可以把廚師當做生產(chǎn)者强窖,(吃飯的人當做消費者)碰逸,而食物則有這樣的過程,被廚師生產(chǎn)出來,然后被來吃飯的人消費奢啥。當食物存在的時候秸仙,廚師等待,不再進行生產(chǎn)桩盲,來吃飯的人進行消費寂纪。當食物為空的時候,廚師開始生產(chǎn)食物赌结,來吃飯的人等待捞蛋。這中間就存在著一個線程之間協(xié)作的過程。
首先是新建兩個線程柬姚,一個廚師線程襟交,一個服務員線程,雙方進行協(xié)作:
以下是廚師線程:
package com.heng.subhey.consumer;
import java.util.concurrent.TimeUnit;
public class Chef implements Runnable{ //product
private Restaurant restaurant;
private int count=0;
public Chef(Restaurant restaurant) {
this.restaurant = restaurant;
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
synchronized (this) {
while (restaurant.meal != null) {
wait();
}
if (++count==10){
System.out.println("out of food,closing");
restaurant.exec.shutdownNow();
}
System.out.println("Chef product meal"+count);
synchronized (restaurant.consumerPerson){
restaurant.meal= new Meal(count);
restaurant.consumerPerson.notifyAll();
}
TimeUnit.MICROSECONDS.sleep(100);
}
}
}catch (InterruptedException e){
System.out.println("Chef interruped");
}
}
}
以下是消費者線程:
package com.heng.subhey.consumer;
public class ConsumerPerson implements Runnable{
private Restaurant restaurant;
public ConsumerPerson(Restaurant restaurant) {
this.restaurant = restaurant;
}
@Override
public void run() {
try{
while (!Thread.interrupted()) {
synchronized (this) {
while (restaurant.meal == null) {
wait();
}
System.out.println("consumerPerson got:"+restaurant.meal);
}
synchronized (restaurant.chef){
restaurant.meal=null;
restaurant.chef.notifyAll();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("consumerPerson interrupdate");
}
}
}
我們來分析一下廚師線程伤靠,主要看它的run方法,里面包含著一個try啼染、catch塊宴合,首先它會一直捕獲線程的狀態(tài),當它不處于interrupted(線程中斷迹鹅,此時無法運轉(zhuǎn))異常時卦洽,往下走。然后取得當前對象的鎖斜棚,把它上鎖阀蒂,通過一個while循環(huán),取得餐館里面的meal弟蚀,判斷其是否為null.當餐館里面的餐還有剩余的時候蚤霞,此時生產(chǎn)者不需要工作,它處于wait狀態(tài)义钉,注意wait和Thread.sleep的區(qū)別昧绣,sleep方法運行的時候線程是不會丟棄鎖的,而wait方法會釋放鎖捶闸,所以此時夜畴,被鎖住的對象運行到wait方法,它已經(jīng)釋放了鎖删壮,因此消費者可以獲取到鎖贪绘。
再接著count會進行累加1。再接著鎖住服務員線程央碟,此時服務員線程獲取鎖的主動權(quán)税灌,它通過notifyAll方法喚醒所有的在鎖上等待線程,注意此處為什么是notifyAll而不是notify,這主要是因為此刻廚師線程并不知道等待的線程究竟是幾條垄琐,為了線程安全起見边酒,喚醒所有的等待線程。注意的是:notifyAll會喚醒所有線程狸窘,但是運行的只是其中一個墩朦,關(guān)于這個線程的篩選,是完全隨機的翻擒。
喚醒了消費者線程氓涣,它就會運行到消費者線程,然后我們來看一下它的run方法陋气,和之前的廚師線程差不多劳吠,依然是鎖住當前的消費者線程,不過線程此時的條件變成了meal為null巩趁。接著再獲取廚師的鎖痒玩,使其meal為null,再喚醒廚師線程议慰,接著程序就會運轉(zhuǎn)到廚師線程蠢古,執(zhí)行廚師的run方法。
package com.heng.subhey.consumer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Restaurant{
Meal meal;
ExecutorService exec = Executors.newCachedThreadPool();
ConsumerPerson consumerPerson =new ConsumerPerson(this);
Chef chef=new Chef(this);
public Restaurant(){
exec.execute(chef);
exec.execute(consumerPerson);
}
public static void main(String[] args){
new Restaurant();
}
}
可以看到結(jié)果别凹,線程有條不紊的運行草讶,生產(chǎn)者每生產(chǎn)一個meal,發(fā)出order up信號,然后消費者消費這個meal炉菲,再輪到生產(chǎn)者堕战,再到消費者,這就是生產(chǎn)者和消費者的模式的意義拍霜,它們之間同步工作嘱丢,不會出現(xiàn)消費者線程沒有meal的時候仍然去消費,生產(chǎn)者在有meal的時候依然去生產(chǎn)祠饺,這樣就會產(chǎn)生線程安全的問題屿讽。