一、生產者消費者模式
生產者消費者模式是通過一個容器來解決生產者和消費者的強耦合問題。生產者和消費者彼此之間不直接通訊,而通過阻塞隊列來進行通訊,所以生產者生產完數據之後不用等待消費者處理,直接扔給阻塞隊列,消費者不找生產者要數據,而是直接從阻塞隊列裏取,阻塞隊列就相當於一個緩衝區,平衡了生產者和消費者的處理能力。
這個阻塞隊列就是用來給生產者和消費者解耦的。縱觀大多數設計模式,都會找一個第三者出來進行解耦,如工廠模式的第三者是工廠類,模板模式的第三者是模板類。在學習一些設計模式的過程中,如果先找到這個模式的第三者,能幫助我們快速熟悉一個設計模式。
二、生產者消費者Demo
生產者
package ConcurrentProgramming.middle.part3.ProviderConsumer;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Author: zdj
* @Description: 生產者
* @Date: 2019年03月18日 9:42
*/
public class Provider implements Runnable {
//共享緩衝區
private BlockingQueue<Data> blockingQueue;
//多線程間是否啓動變量,即使返回線程的狀態
private volatile static boolean isRunning = true;
private static AtomicInteger count = new AtomicInteger();
//隨機對象
private static Random r = new Random();
public Provider(BlockingQueue blockingQueue) {
this.blockingQueue = blockingQueue;
}
public void run() {
while (isRunning) {
try {
//隨機休眠0 - 1000 毫秒 表示獲取數據(產生數據的耗時)
Thread.sleep(r.nextInt(1000));
//獲取的數據進行累加
int id = count.incrementAndGet();
Data data = new Data(Integer.toString(id), "數據" + id);
System.out.println("線程" + Thread.currentThread().getName() + ",獲取了數據,id爲:" + id + ", 進行裝載到公共緩衝區中...");
if (!this.blockingQueue.offer(data,2, TimeUnit.SECONDS)){
System.out.println("提交緩衝區數據失敗....");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void stop(){
isRunning = false;
}
}
消費者
package ConcurrentProgramming.middle.part3.ProviderConsumer;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
/**
* @Author: zdj
* @Description: 消費者
* @Date: 2019年03月18日 9:53
*/
public class Consumer implements Runnable{
//共享緩衝區
private BlockingQueue<Data> blockingQueue;
private volatile boolean isRunning = true;
public Consumer(BlockingQueue blockingQueue){
this.blockingQueue = blockingQueue;
}
private static Random r = new Random();
public void run() {
while (isRunning){
try {
//隊列中沒有數據,就阻塞線程
Data data = this.blockingQueue.take();
//模擬對數據的處理
Thread.sleep(r.nextInt(1000));
System.out.println("當前消費線程:" + Thread.currentThread().getName() +
"消費成功,數據id爲" + data.getId() );
}catch (Exception e){
e.printStackTrace();
}
}
}
public void stop(){
this.isRunning = false;
}
}
模擬數據
package ConcurrentProgramming.middle.part3.ProviderConsumer;
/**
* @Author: zdj
* @Description:
* @Date: 2019年03月18日 9:41
*/
@lombok.Data
public class Data {
private String id;
private String name;
public Data(String id, String name) {
this.id = id;
this.name = name;
}
}
測試Main
package ConcurrentProgramming.middle.part3.ProviderConsumer;
import java.util.concurrent.*;
/**
* @Author: zdj
* @Description:
* @Date: 2019年03月18日 13:38
*/
public class Main {
public static void main(String[] args) {
BlockingQueue<Data> blockingQueue = new LinkedBlockingQueue<Data>(10);
//生產者
Provider provider1 = new Provider(blockingQueue);
Provider provider2 = new Provider(blockingQueue);
Provider provider3 = new Provider(blockingQueue);
//消費者
Consumer consumer1 = new Consumer(blockingQueue);
Consumer consumer2 = new Consumer(blockingQueue);
Consumer consumer3 = new Consumer(blockingQueue);
//創建線程池運行,這是一個緩存的線程池,可以創建無窮大的線程,沒有任務的時候不創建線程。空閒線程存活時間爲60s(默認值)
ExecutorService cachePool = Executors.newCachedThreadPool();
cachePool.execute(provider1);
cachePool.execute(provider2);
cachePool.execute(provider3);
cachePool.execute(consumer1);
cachePool.execute(consumer2);
cachePool.execute(consumer3);
try {
Thread.sleep(2000);
}catch (Exception e){
e.printStackTrace();
}
provider1.stop();
provider2.stop();
provider3.stop();
consumer1.stop();
consumer2.stop();
consumer3.stop();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cachePool.shutdown();
}
}
運行結果
借鑑鏈接:http://ifeve.com/producers-and-consumers-mode/