1. 程式人生 > >多執行緒初探之生產者消費者

多執行緒初探之生產者消費者

前言

對於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來控制的執行緒間通訊。

這裡寫圖片描述
這裡寫圖片描述