一、什麼是生產者消費者模式?
生產者/消費者模式是為了解耦消費者和生產者而產生的,其原理非常地簡單。總的來說就是生產者和消費者之間不直接通訊,而是藉助一個第三方(通常是阻塞佇列)
,第三方也成為臨界資源,同一時間只允許一條執行緒對其進行操作。
- 1、當臨界資源滿了,生產者必須阻塞等待;
- 2、當臨界資源為空,消費者必須阻塞等待,通知生產者生產;
二、使用簡單的notify/wait
機制實現
所有的註釋都寫在程式碼中,在這裡我們模仿在水桶中存水和取水的過程:
Main.java
package com.wokao66.consumerProvider;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 測試
* @author: huangjiawei
* @since: 2018年4月3日
* @version: $Revision$ $Date$ $LastChangedBy$
*/
public class Main {
public static void main(String[] args) {
/**
* 新建一個水桶,存放所有的水,剛開始水桶是空的,容量為5L
*/
List<Water> waterList = new ArrayList<>(5);
ExecutorService executors = Executors.newFixedThreadPool(10);
WaterProvider provider = new WaterProvider(waterList);
WaterConsumer consumer = new WaterConsumer(waterList);
executors.execute(provider);
executors.execute(consumer);
}
}
複製程式碼
WaterProvider.java
package com.wokao66.consumerProvider;
import java.util.List;
/**
* 往桶裡加水的生產者
* @author: huangjiawei
* @since: 2018年4月3日
* @version: $Revision$ $Date$ $LastChangedBy$
*/
public class WaterProvider implements Runnable {
/**
* 這是我們的水桶(10L)
*/
private List<Water> waterList = null;
/**
* 初始化水桶,也就是緩衝區
*/
public WaterProvider(List<Water> waterList) {
this.waterList = waterList;
}
@Override
public void run() {
/**
* 迴圈任務,也就是這個任務會執行多次,沒有明確的break語句或者異常,該任務不會終止
*/
while (true) {
/**
* 這裡獲得waterList的鎖,之前說過notify、wait的使用必須先獲得鎖
*/
synchronized (waterList) {
/**
* 判斷是不是滿了,滿了就不生產了
*/
while (waterList.size() == 5) {
try {
/**
* 這裡將所釋放掉waterList的鎖
*/
waterList.wait();
} catch (InterruptedException e) {}
}
/**
* 如果還沒有滿,那麼就加1L水進去,加進去之前
*/
waterList.add(new Water());
System.err.println("生產了1L水,現在水桶有:" + waterList.size() + "L水");
try {
/**
* sleep方法是不會釋放鎖的
*/
Thread.sleep(1000);
} catch (InterruptedException e) {}
/**
* 我通知所有的消費者來消費
*/
waterList.notifyAll();
}
}
}
}
複製程式碼
WaterConsumer.java
package com.wokao66.consumerProvider;
import java.util.List;
/**
* 往桶裡取水的消費者
* @author: huangjiawei
* @since: 2018年4月3日
* @version: $Revision$ $Date$ $LastChangedBy$
*/
public class WaterConsumer implements Runnable {
private List<Water> waterList;
public WaterConsumer(List<Water> waterList) {
this.waterList = waterList;
}
@Override
public void run() {
/**
* 迴圈任務,也就是這個任務會執行多次,沒有明確的break語句或者異常,該任務不會終止
*/
while (true) {
/**
* 獲得鎖
*/
synchronized (waterList) {
/**
* 證明沒有水可以消費
*/
while (waterList.isEmpty()) {
try {
/**
* 釋放鎖
*/
waterList.wait();
} catch (InterruptedException e) {}
}
/**
* 每次我都移動第一個元素
*/
waterList.remove(0);
System.err.println("消費了1L水,現在水桶有:" + waterList.size() + "L水");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
/**
* 通知生產者生產
*/
waterList.notifyAll();
}
}
}
}
複製程式碼
Water.java
package com.wokao66.consumerProvider;
/**
* 水這種型別
* @author: huangjiawei
* @since: 2018年4月3日
* @version: $Revision$ $Date$ $LastChangedBy$
*/
public class Water {
/**
* 單位L
*/
private String unit;
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
}
複製程式碼
執行結果:
生產了1L水,現在水桶有:1L水
生產了1L水,現在水桶有:2L水
消費了1L水,現在水桶有:1L水
消費了1L水,現在水桶有:0L水
生產了1L水,現在水桶有:1L水
生產了1L水,現在水桶有:2L水
消費了1L水,現在水桶有:1L水
消費了1L水,現在水桶有:0L水
生產了1L水,現在水桶有:1L水
消費了1L水,現在水桶有:0L水
生產了1L水,現在水桶有:1L水
生產了1L水,現在水桶有:2L水
生產了1L水,現在水桶有:3L水
生產了1L水,現在水桶有:4L水
生產了1L水,現在水桶有:5L水
消費了1L水,現在水桶有:4L水
消費了1L水,現在水桶有:3L水
消費了1L水,現在水桶有:2L水
消費了1L水,現在水桶有:1L水
複製程式碼
執行結果可能會不一致,但資料正確即可!