Java實現生產者和消費者模式(多線程)

1、生產者和消費者模式描述 

生產者和消費者問題是線程模型中的經典問題:生產者和消費者在同一時間段內共用同一存儲空間,生產者向空間裏生產數據,而消費者從空間裏取走數據。

2、存在的問題 

  • 當生產者比消費者快時,消費者會漏掉一部分數據; 
  • 當消費者比生產者快時,消費者會取走相同的數據。 
解決方式:要考慮線程安全的問題,解決此問題有三種方式 

同步代碼塊 
同步方法  
lock鎖機制, 經過建立Lock對象,採用lock()加鎖,unlock()解鎖,來保護指定的代碼塊複製代碼

此文采用第二種方式解決了線程安全問題  java

3、wait()和notify()方法 

wait()、nofity()這兩個方法必須有鎖對象調用,而任意對象均可以做爲 synchronized 的同步鎖,所以這三個方法只能在Object 類中聲明 。  

wait():當緩衝區已滿/空時,生產者或消費者線程中止本身的執行,釋放鎖,使本身處於等待狀態,讓其它線程執行。  安全

notify():當生產者或消費者向緩衝區放入或取出一個產品時,向其餘等待的線程發出通知,同時釋放鎖,使本身處於等待狀態,讓其它線程執行。  bash

4、生產者和消費者模式中涉及到的類 

商店類(Shop):定義一個成員變量,表示第幾個麪包,提供生產麪包和消費麪包的操做; 
生產者類(Producer):實現Runnable接口,重寫run()方法,調用生產麪包的操做; 
消費者類(Consumer):實現Runnable接口,重寫run()方法,調用消費麪包的操做; 
測試類: 

5、代碼實現 

/**
 * 商店類(Shop):定義一個成員變量,表示第幾個麪包,提供生產麪包和消費麪包的操做;
 */
class Shop {
    private int bread = 0;
    /**
     * 生產麪包
     */
    public synchronized void produceBread() {
        if (bread < 10) {
            bread++;
            System.out.println(Thread.currentThread().getName() + ":開始生產第" + bread + "個麪包");
            notify(); // 喚醒消費者線程
        } else {
            try {
                wait(); // 告訴生產者等待一下
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    /**
     * 消費麪包
     */
    public synchronized void consumeBread() {
        if (bread > 0) {
            System.out.println(Thread.currentThread().getName() + ":開始消費第" + bread + "個麪包");
            bread--;
            notify(); // 喚醒生產者線程
        } else {
            try {
                wait(); // 告訴消費者等待一下
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

/**
 * 生產者類(Producer):實現Runnable接口,重寫run()方法,調用生產麪包的操做
 */
class Producer extends Thread {
    private Shop shop;

    public Producer(Shop shop) {
        this.shop = shop;
    }

    @Override
    public void run() {
        System.out.println(getName() + ":開始生產麪包.....");
        while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            shop.produceBread();
        }
    }
}

/**
 * 消費者類(Consumer):實現Runnable接口,重寫run()方法,調用消費麪包的操做
 */
class Consumer extends Thread {
    private Shop shop;
    public Consumer(Shop shop) {
        this.shop = shop;
    }
    
    @Override
    public void run() {
        System.out.println(getName() + ":開始消費麪包.....");
        while (true) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            shop.consumeBread();
        }
    }
}

public class BreadTest {
    public static void main(String[] args) {
        // 建立商店對象
        Shop shop = new Shop();
        // 建立生產者對象,把商店對象做爲構造方法參數傳入生產者對象中
        Producer p1 = new Producer(shop);
        p1.setName("生產者");
        // 建立消費者對象,把商店對象做爲構造方法參數傳入消費者對象中
        Consumer c1 = new Consumer(shop);
        c1.setName("消費者");

        p1.start();
        c1.start();
        
    }
}複製代碼

代碼執行結果以下:  ide

消費者:開始消費麪包.....
生產者:開始生產麪包.....
生產者:開始生產第1個麪包
生產者:開始生產第2個麪包
生產者:開始生產第3個麪包
生產者:開始生產第4個麪包
消費者:開始消費第4個麪包
生產者:開始生產第4個麪包
生產者:開始生產第5個麪包
生產者:開始生產第6個麪包
生產者:開始生產第7個麪包
生產者:開始生產第8個麪包
消費者:開始消費第8個麪包
生產者:開始生產第8個麪包
生產者:開始生產第9個麪包
生產者:開始生產第10個麪包
消費者:開始消費第10個麪包
生產者:開始生產第10個麪包
消費者:開始消費第10個麪包
生產者:開始生產第10個麪包
消費者:開始消費第10個麪包
生產者:開始生產第10個麪包
消費者:開始消費第10個麪包

...
複製代碼

最後

歡迎你們有興趣的能夠關注個人公衆號【java小瓜哥的分享平臺】,文章都會在裏面更新,還有各類java的資料都是免費分享的。但願與你們一塊兒進步努力!測試

相關文章
相關標籤/搜索