Java多執行緒——生產者和消費者模式
生產者消費者模式
生產者和消費者模式是一種併發設計模式,生產者消費者模式解決的是兩者速率不一致而產生的阻抗不匹配,該模式通過平衡生產執行緒和消費執行緒的工作能力來提高程式的整體處理資料的速度。
生產者消費者模式是通過一個容器來解決生產者和消費者的強耦合問題。生產者和消費者彼此之間不直接通訊,而通過阻塞佇列來進行通訊,所以生產者生產完資料之後不用等待消費者處理,直接扔給阻塞佇列,消費者不找生產者要資料,而是直接從阻塞佇列裡取,阻塞佇列就相當於一個緩衝區,平衡了生產者和消費者的處理能力。
為什麼要使用生產者和消費者模式
在多執行緒開發當中,如果生產者處理速度很快,而消費者處理速度很慢,那麼生產者就必須等待消費者處理完,才能繼續生產資料。同樣的道理,如果消費者的處理能力大於生產者,那麼消費者就必須等待生產者。為了解決這個問題於是引入了生產者和消費者模式。
優點
可以獨立地同時編碼生產者和消費者,他們只需要知道共享物件即可。
生產者不需要知道誰是消費者或有多少消費者,消費者也是如此。
生產者和消費者可以以不同的速度工作,消費者沒有消費半成品的風險。
分離生產者和消費者的功能導致更乾淨,可讀和易於管理的程式碼。
應用
Executor框架本身也實現了生產者和消費者模式,線上程池中,如果任務數多於基本執行緒數時,會將任務放到阻塞佇列中來平衡生產者和消費者的處理能力,關於執行緒池的介紹可以看我的另一篇文章 java——執行緒池
示例程式碼
用阻塞佇列實現
先用阻塞佇列來實現,BlockingQueue 是個繼承Queue介面的介面,該介面有不同的實現,比如ArrayBlockingQueue 和 LinkedBlockingQueue,他們都實現了 FIFO。
用LinkedBlockingQueue實現生產者和消費者模式如下。
public class ProducerConsumerPractice {
public static void main(String[] args){
LinkedBlockingDeque<Integer> linkedBlockingDeque = new LinkedBlockingDeque<>(5);
new Thread(new Producer(linkedBlockingDeque)).start();
new Thread(new Consumer(linkedBlockingDeque)).start();
}
}
class Producer implements Runnable{
private LinkedBlockingDeque<Integer> linkedBlockingDeque;
public Producer(LinkedBlockingDeque<Integer> linkedBlockingDeque){
this.linkedBlockingDeque = linkedBlockingDeque;
}
public void run(){
for(int i = 0; i < 10; i++){
try {
//Thread.sleep(500);
linkedBlockingDeque.put(i);
System.out.println("Producer: " + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable{
private LinkedBlockingDeque<Integer> linkedBlockingDeque;
public Consumer(LinkedBlockingDeque<Integer> linkedBlockingDeque){
this.linkedBlockingDeque = linkedBlockingDeque;
}
public void run(){
while(true){
try{
Thread.sleep(500);
System.out.println("consumer: " + linkedBlockingDeque.take());
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
執行結果:
Producer: 0
Producer: 1
Producer: 2
Producer: 3
Producer: 4
consumer: 0
Producer: 5
consumer: 1
Producer: 6
consumer: 2
Producer: 7
consumer: 3
Producer: 8
consumer: 4
Producer: 9
consumer: 5
consumer: 6
consumer: 7
consumer: 8
consumer: 9
我設定了阻塞佇列的初始長度為5,然後用sleep(500)調慢了消費速度,所以我們在執行結果中可以看到生產0-4後,佇列滿了,生產者被阻塞了,然後消費者根據FIFO原則先消費了0,所以生產者又可以繼續生產了。在ide中執行看的會更清楚些,第二種方式實現列印的結果會更明白。
用wait(), notify()實現
之前寫過一篇文章 執行緒間協作——wait、notify、notifyAll 講了 wait(), notify(),notifyAll()的用法,現在用他們來實現生產者和消費者模式,當做補充例子吧。這裡用 Vector 模擬佇列,因為這個佇列沒有阻塞功能,所以要用wait()和 notify()模擬佇列滿時生產者和佇列為空時消費者的阻塞,以及正常情況下互相通知對方的效果。
程式碼中同樣調慢了消費速度,為了看的更清晰。
public class ProducerConsumerPractice {
public static void main(String[] args){
Vector<Integer> vector = new Vector<>(5);
new Thread(new Producer(vector)).start();
new Thread(new Consumer(vector)).start();
}
}
class Producer implements Runnable{
private Vector<Integer> vector;
public Producer(Vector vector){
this.vector = vector;
}
public void run(){
for(int i = 0; i < 10; i++){
while(vector.size() == vector.capacity()){
synchronized (vector){
System.out.println("Queue is full, Producer is waiting , size: " + vector.size());
try {
vector.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized (vector){
vector.add(i);
System.out.println("Producer: " + i);
vector.notifyAll();
}
}
}
}
class Consumer implements Runnable{
private Vector<Integer> vector;
public Consumer(Vector vector){
this.vector = vector;
}
public void run(){
while(true){
while(vector.isEmpty()){
synchronized (vector){
System.out.println("Queue is empty, Consumer is waiting , size: " + vector.size());
try {
vector.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//調慢消費速度
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (vector){
System.out.println("Consumer: " + vector.remove(0));
vector.notifyAll();
}
}
}
}
執行結果:
Producer: 0
Producer: 1
Producer: 2
Producer: 3
Producer: 4
Queue is full, Producer is waiting , size: 5
Consumer: 0
Producer: 5
Queue is full, Producer is waiting , size: 5
Consumer: 1
Producer: 6
Queue is full, Producer is waiting , size: 5
Consumer: 2
Producer: 7
Queue is full, Producer is waiting , size: 5
Consumer: 3
Producer: 8
Queue is full, Producer is waiting , size: 5
Consumer: 4
Producer: 9
Consumer: 5
Consumer: 6
Consumer: 7
Consumer: 8
Consumer: 9
Queue is empty, Consumer is waiting , size: 0
參考資料
聊聊併發——生產者消費者模式
Producer Consumer Problem with Wait and Notify Example
相關文章
- Java多執行緒——生產者消費者示例Java執行緒
- Java 多執行緒(Java.Thread)------ 執行緒協作(生產者消費者模式)Java執行緒thread模式
- Java 多執行緒學習(執行緒通訊——消費者和生產者)Java執行緒
- 生產者消費者模式--java多執行緒同步方法的應用模式Java執行緒
- java多執行緒:執行緒間通訊——生產者消費者模型Java執行緒模型
- Java 多執行緒基礎(十二)生產者與消費者Java執行緒
- Java多執行緒——消費者與生產者的關係Java執行緒
- python多執行緒+生產者和消費者模型+queue使用Python執行緒模型
- C#多執行緒學習(三) 生產者和消費者C#執行緒
- python執行緒通訊與生產者消費者模式Python執行緒模式
- Python-多執行緒及生產者與消費者Python執行緒
- python中多執行緒消費者生產者問題Python執行緒
- 使用Python佇列和多執行緒實現生產者消費者Python佇列執行緒
- python 多執行緒實現生產者與消費者模型Python執行緒模型
- 執行緒間的協作(2)——生產者與消費者模式執行緒模式
- 多執行緒併發如何高效實現生產者/消費者?執行緒
- java多執行緒之消費生產模型Java執行緒模型
- Java多執行緒程式設計(同步、死鎖、生產消費者問題)Java執行緒程式設計
- java學習回顧---生產者與消費者問題以及多執行緒補充Java執行緒
- 生產者消費者模式模式
- java 執行緒池、多執行緒併發實戰(生產者消費者模型 1 vs 10) 附案例原始碼Java執行緒模型原始碼
- 生產消費者模式模式
- python 生產者消費者模式Python模式
- Java實現生產者和消費者Java
- 阻塞佇列和生產者-消費者模式佇列模式
- java編寫生產者/消費者模式的程式。Java模式
- 九、生產者與消費者模式模式
- 使用BlockQueue實現生產者和消費者模式BloC模式
- Thinking in Java---執行緒通訊+三種方式實現生產者消費者問題ThinkingJava執行緒
- 多執行緒必考的「生產者 - 消費者」模型,看齊姐這篇文章就夠了執行緒模型
- 併發設計模式---生產者/消費者模式設計模式
- Java中的設計模式(二):生產者-消費者模式與觀察者模式Java設計模式
- ActiveMQ 生產者和消費者demoMQ
- 生產者消費者
- 母雞下蛋例項:多執行緒通訊生產者和消費者wait/notify和condition/await/signal條件佇列執行緒AI佇列
- java進階(40)--wait與notify(生產者與消費者模式)JavaAI模式
- 從一次生產消費者的bug看看執行緒池如何增加執行緒執行緒
- 執行緒同步介紹及 生產者消費者問題舉例 C#版執行緒C#
- 生產者消費者模型模型