併發設計模式---生產者/消費者模式

擁抱心中的夢想發表於2018-04-03

一、什麼是生產者消費者模式?

生產者/消費者模式是為了解耦消費者和生產者而產生的,其原理非常地簡單。總的來說就是生產者和消費者之間不直接通訊,而是藉助一個第三方(通常是阻塞佇列),第三方也成為臨界資源,同一時間只允許一條執行緒對其進行操作。

  • 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水
複製程式碼

執行結果可能會不一致,但資料正確即可!

相關文章