多執行緒初探之生產者消費者
阿新 • • 發佈:2018-12-13
前言
對於java開者而言,多執行緒開發是不可避免的,多執行緒程式相對於單執行緒程式穩定性更強,一個執行緒掛了不會影響整個程式的正常執行。在多cup的機器上,多執行緒更加具有效率上的優勢。但是多執行緒也會導致一些問題,集中出現在資料方面。當多個執行緒操作同一個資料來源的時候就會出現讀髒的問題,如何在多執行緒下保證資料的原子性和可見性,就顯得格外的重要。下面就為大家來解析生產者消費者模式。
建立管理類
Manager 類負責生產和消費者兩種動作。在product方法中,當num的值大於100時,結束迴圈,最後一次執行完畢後任務結束。c代表生產多少後開始wait然後喚醒當前鎖執行緒池中等待的其他執行緒去爭奪當前鎖。如果未達到生產要求則繼續執行,不會釋放鎖。其他執行緒任然是等待,這裡不需要喚醒其他執行緒,因為喚醒的話也沒用,不會釋放鎖,因為此程式碼是在同步快中,執行緒是同步執行的。
consume方法是消費者的方法,此方法比較簡單,就是噹噹前產品的數量滿足自己的消費量時進行消費,並且該執行緒就結束了。否則會wait,這裡要先執行notifyAll()方法,來喚醒等待執行緒,再wait。
package com.yzz.t.Thread;
public class Manager {
private int num = 0;
public synchronized void product(int c) throws Exception {
int count = 0;
while (num<100 ) {
if (count == c) {
System.out.println("生產者"+Thread.currentThread().getName()+":生產了" + count + "件,總件數為" + num + "這裡暫停");
// notifa或者notifaAll必須在wait之前呼叫。
this.notifyAll();
count = 0;
// 執行wait,立即釋放鎖,等待再次獲得鎖,在繼續往下執行
this.wait();
}
Thread.sleep(500);
// 生產出2件後
num++;
count++;
System.out.println("生產者"+Thread.currentThread().getName()+":生產了" + count + "件,總件數為" + num + "繼續生產");
}
}
public synchronized void consume(int need) throws Exception {
while (num < need) {
System.out.println("消費者"+Thread.currentThread().getName()+":需要" + need + "件,總件數為" + num + "不夠等待");
this.notifyAll();
this.wait();
}
num -= need;
System.out.println("消費者"+Thread.currentThread().getName()+":消費" + need + "件,剩餘" + num + "消費離開");
}
}
生產者和消費者
這裡採用的是整合Thread的方式來寫執行緒,其實實現Runable介面和繼承Thread類沒有本質上的區別,唯一的區別體現在多繼承的機況下,就會出現問題了,所有實現Runable介面就體現出優勢來了。
package com.yzz.t.Thread;
public class Producter extends Thread{
private Manager manager;
private int c;
public Producter(Manager manager,int c,String name) {
super();
this.manager = manager;
setName(name);
this.c = c;
}
@Override
public void run() {
try {
super.run();
manager.product(c);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.yzz.t.Thread;
public class Consumer extends Thread{
private Manager manager;
private int need;
public Consumer(Manager manager, int need,String name) {
super();
this.manager = manager;
this.need = need;
setName(name);
}
@Override
public void run() {
try {
super.run();
manager.consume(need);
} catch (Exception e) {
e.printStackTrace();
}
}
}
測試
這裡開啟了兩個執行緒去充當生產者,六個執行緒去充當消費者,生產者的生產能力和消費者的消費能力都不一樣。
package com.yzz.t.test;
import com.yzz.t.Thread.Consumer;
import com.yzz.t.Thread.Manager;
import com.yzz.t.Thread.Producter;
public class Test {
public static void main(String[] args) {
Manager manager = new Manager();
Producter producter1 = new Producter(manager,2,"生產者1");
Producter producter2 = new Producter(manager,1,"生產者2");
Producter producter3 = new Producter(manager,1,"生產者3");
Consumer consumer1 = new Consumer(manager, 12, "消費者1");
Consumer consumer2 = new Consumer(manager, 23, "消費者2");
Consumer consumer3 = new Consumer(manager, 1, "消費者3");
Consumer consumer4 = new Consumer(manager, 3, "消費者4");
Consumer consumer5 = new Consumer(manager, 8, "消費者5");
Consumer consumer6 = new Consumer(manager, 46, "消費者6");
producter1.start();
producter2.start();
producter3.start();
consumer1.start();
consumer2.start();
consumer3.start();
consumer4.start();
consumer5.start();
consumer6.start();
}
}
結果
生產者和消費者模式只是一種思想,沒有固定的模式,根據具體的業務需求也有不同的形式,但是最終都是通過在同步環境下使用wait和notifaAll來控制的執行緒間通訊。