溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java實現(xiàn)生產(chǎn)者消費者的兩種方式分別是什么

發(fā)布時間:2021-11-16 11:56:28 來源:億速云 閱讀:125 作者:柒染 欄目:編程語言

本篇文章給大家分享的是有關Java實現(xiàn)生產(chǎn)者消費者的兩種方式分別是什么,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

   我在8年前去面試程序員的時候,一個不大的公司,里面的開發(fā)主管接待了我們,給我的題目就是寫一段程序模擬生產(chǎn)者消費者問題,當時可把我難壞了,一下子感覺自己的知識儲備竟然如此的匱乏。

   而在我從事DBA工作之后,經(jīng)常會有大批量并發(fā)的環(huán)境,有的需要排障,有的需要優(yōu)化,在很多并發(fā)的場景中,發(fā)現(xiàn)生產(chǎn)者消費者問題可以模擬出很多實際中的問題,所以生產(chǎn)者消費者問題非常重要,也是我想不斷改進和探索的一類問題。

引入倉庫的必要性

   要想使用程序來模擬,其實也不用花太多的時間,我們簡單說說需要考慮的地方。首先生產(chǎn)者,消費者是兩個實體對象,生產(chǎn)者生產(chǎn)物品,消費者消費物品,如果在生產(chǎn)者中定義生產(chǎn)的流程,在消費者中定義消費的流程,兩個對象就需要彼此引用,依賴性太高,而且實際上性能也好不到哪里去,所以就需要一個緩沖器,一個中間對象,我們就叫做倉庫吧,生產(chǎn)的物品推入倉庫,消費的物品從倉庫中取出,這樣生產(chǎn)者和消費者就能夠取消之間的引用,直接通過倉庫引用來同步狀態(tài),降低耦合。

    所以我們的一個初步設想就是生產(chǎn)者-->倉庫<--消費者 這樣的模式。

生產(chǎn)者消費者的幾種類型和實現(xiàn)方式

    當然生產(chǎn)者消費者問題有兩種類型,一種就是使用某種機制來保護生產(chǎn)者和消費者之間的同步,另外一種和Linux中的管道思路相似。相對來說第一種類型的處理方式更為通用,大體分為三類具體的實現(xiàn)方式:

  1. 經(jīng)典的wait(),notify()方法

  2. await(),signal()方法

  3. 使用阻塞隊列(BlockingQueue),比如LinkedBlockingQueue

通用的對象類

為了更快出成果,盡可能快速理解,我也參考了一些資料,下午下班前寫了下面的程序。我就簡單說明第一種和第二種吧。

  因為實體類對象是通用的,我就不再重復列出了,有生產(chǎn)者Producer和消費者Consumer兩個類。

生產(chǎn)者類

import com.jeanron.test1.Storage;

public class Producer extends Thread {
    // 每次生產(chǎn)的產(chǎn)品數(shù)量
    private int num;

    // 所在放置的倉庫
    private Storage storage;

    // 構造函數(shù),設置倉庫
    public Producer(Storage storage) {
        this.storage = storage;
    }

    public Producer(Storage storage, int num) {
        this.storage = storage;
        this.num = num;
    }

    // 線程run函數(shù)
    public void run() {
        produce(num);
    }

    // 調(diào)用倉庫Storage的生產(chǎn)函數(shù)
    public void produce(int num) {
        storage.produce(num);
    }

    // get/set方法
    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public Storage getStorage() {
        return storage;
    }

    public void setStorage(Storage storage) {
        this.storage = storage;
    }
}

消費者類

import com.jeanron.test1.Storage;
 
public class Consumer extends Thread  
{  
    // 每次消費的產(chǎn)品數(shù)量  
    private int num;  
 
    // 所在放置的倉庫  
    private Storage storage;  
 
    // 構造函數(shù),設置倉庫  
    public Consumer(Storage storage)  
    {  
        this.storage = storage;  
    }  
    
    // 構造函數(shù),設置倉庫  
    public Consumer(Storage storage,int num)  
    {  
        this.storage = storage;  
        this.num = num;
    }  
 
    // 線程run函數(shù)  
    public void run()  
    {  
        consume(num);  
    }  
 
    // 調(diào)用倉庫Storage的生產(chǎn)函數(shù)  
    public void consume(int num)  
    {  
        storage.consume(num);  
    }  
 
    // get/set方法  
    public int getNum()  
    {  
        return num;  
    }  
 
    public void setNum(int num)  
    {  
        this.num = num;  
    }  
 
    public Storage getStorage()  
    {  
        return storage;  
    }  
 
    public void setStorage(Storage storage)  
    {  
        this.storage = storage;  
    }  
}  滴喲中

第一種實現(xiàn)方式

import java.util.LinkedList;

public class Storage  
{  
    // 倉庫最大存儲量  
    private final int MAX_SIZE = 200;  
 
    // 倉庫存儲的載體  
    private LinkedList<Object> list = new LinkedList<Object>();  
    private static Storage storage=null;  
    public static Storage getInstance() {  
        if (storage == null) {    
            synchronized (Storage.class) {    
               if (storage == null) {    
                   storage = new Storage();   
               }    
            }    
        }    
        return storage;   
    }   
    
    // 生產(chǎn)num個產(chǎn)品  
    public void produce(int num)  
    {  
        // 同步代碼段  
        synchronized (list)  
        {  
            // 如果倉庫剩余容量不足  
            while (list.size() + num > MAX_SIZE)  
            {  
                System.out.println(Thread.currentThread().getName()+"【生產(chǎn)者:要生產(chǎn)的產(chǎn)品數(shù)量】:" + num + "\t【庫存量】:"  
                        + list.size() + "\t暫時不能執(zhí)行生產(chǎn)任務!");  
                try  
                {  
                    // 由于條件不滿足,生產(chǎn)阻塞  
                    list.wait();  
                }  
                catch (InterruptedException e)  
                {  
                    e.printStackTrace();  
                }  
            }  
 
            // 生產(chǎn)條件滿足情況下,生產(chǎn)num個產(chǎn)品  
            for (int i = 1; i <= num; ++i)  
            {  
                list.add(new Object());  
            }  
 
            System.out.println(Thread.currentThread().getName()+"【生產(chǎn)者:已經(jīng)生產(chǎn)產(chǎn)品數(shù)】:" + num + "\t【現(xiàn)倉儲量為】:" + list.size());  
 
            list.notifyAll();  
        }  
    }  
 
    // 消費num個產(chǎn)品  
    public void consume(int num)  
    {  
        // 同步代碼段  
        synchronized (list)  
        {  
            // 如果倉庫存儲量不足  
            while (list.size() < num)  
            {  
                System.out.println(Thread.currentThread().getName()+"【消費者:要消費的產(chǎn)品數(shù)量】:" + num + "\t【庫存量】:"  
                        + list.size() + "\t暫時不能執(zhí)行消費任務!");  
                try  
                {  
                    // 由于條件不滿足,消費阻塞  
                    list.wait();  
                }  
                catch (InterruptedException e)  
                {  
                    e.printStackTrace();  
                }  
            }  
 
            // 消費條件滿足情況下,消費num個產(chǎn)品  
            for (int i = 1; i <= num; ++i)  
            {  
                list.remove();  
            }  
 
            System.out.println(Thread.currentThread().getName()+"【消費者:已經(jīng)消費產(chǎn)品數(shù)】:" + num + "\t【現(xiàn)倉儲量為】:" + list.size());  
 
            list.notifyAll();  
        }  
    }  
 
    // get/set方法  
    public LinkedList<Object> getList()  
    {  
        return list;  
    }  
 
    public void setList(LinkedList<Object> list)  
    {  
        this.list = list;  
    }  
 
    public int getMAX_SIZE()  
    {  
        return MAX_SIZE;  
    }  
}  

第二種實現(xiàn)方式

第二種使用await和signal的方式實現(xiàn),我們使用Lock來產(chǎn)生兩個Condition對象來管理任務間的通信,一個重要的參考點就是倉庫滿,倉庫空,這類方式最后必須有try-finally的子句,保證能夠釋放鎖。

package com.jeanron.test2;

import java.util.LinkedList;  
import java.util.concurrent.locks.Condition;  
import java.util.concurrent.locks.Lock;  
import java.util.concurrent.locks.ReentrantLock;  

import com.jeanron.test2.Storage;
 
 
public class Storage  
{  
    // 倉庫最大存儲量  
    private final int MAX_SIZE = 200;  
 
    // 倉庫存儲的載體  
    private LinkedList<Object> list = new LinkedList<Object>();  
    
    private static Storage storage=null;  
    public static Storage getInstance() {  
        if (storage == null) {    
            synchronized (Storage.class) {    
               if (storage == null) {    
                   storage = new Storage();   
               }    
            }    
        }    
        return storage;   
    }   
    
 
    // 鎖  
    private final Lock lock = new ReentrantLock();  
 
    // 倉庫滿的條件變量  
    private final Condition full = lock.newCondition();  
 
    // 倉庫空的條件變量  
    private final Condition empty = lock.newCondition();  
 
    // 生產(chǎn)num個產(chǎn)品  
    public void produce(int num)  
    {  
        // 獲得鎖  
        lock.lock();  
 
        // 如果倉庫剩余容量不足  
        while (list.size() + num > MAX_SIZE)  
        {  
            System.out.println(Thread.currentThread().getName()+"【要生產(chǎn)的產(chǎn)品數(shù)量】:" + num + "\t【庫存量】:" + list.size()  
                    + "\t暫時不能執(zhí)行生產(chǎn)任務!");  
            try  
            {  
                // 由于條件不滿足,生產(chǎn)阻塞  
                full.await();  
            }  
            catch (InterruptedException e)  
            {  
                e.printStackTrace();  
            }  
        }  
 
        // 生產(chǎn)條件滿足情況下,生產(chǎn)num個產(chǎn)品  
        for (int i = 1; i <= num; ++i)  
        {  
            list.add(new Object());  
        }  
 
        System.out.println(Thread.currentThread().getName()+"【已經(jīng)生產(chǎn)產(chǎn)品數(shù)】:" + num + "\t【現(xiàn)倉儲量為】:" + list.size());  
 
        // 喚醒其他所有線程  
        full.signalAll();  
        empty.signalAll();  
 
        // 釋放鎖  
        lock.unlock();  
    }  
 
    // 消費num個產(chǎn)品  
    public void consume(int num)  
    {  
        // 獲得鎖  
        lock.lock();  
 
        // 如果倉庫存儲量不足  
        while (list.size() < num)  
        {  
            System.out.println(Thread.currentThread().getName()+"【要消費的產(chǎn)品數(shù)量】:" + num + "\t【庫存量】:" + list.size()  
                    + "\t暫時不能執(zhí)行生產(chǎn)任務!");  
            try  
            {  
                // 由于條件不滿足,消費阻塞  
                empty.await();  
            }  
            catch (InterruptedException e)  
            {  
                e.printStackTrace();  
            }  
        }  
 
        // 消費條件滿足情況下,消費num個產(chǎn)品  
        for (int i = 1; i <= num; ++i)  
        {  
            list.remove();  
        }  
 
        System.out.println(Thread.currentThread().getName()+"【已經(jīng)消費產(chǎn)品數(shù)】:" + num + "\t【現(xiàn)倉儲量為】:" + list.size());  
 
        // 喚醒其他所有線程  
        full.signalAll();  
        empty.signalAll();  
 
        // 釋放鎖  
        lock.unlock();  
    }  
 
    // set/get方法  
    public int getMAX_SIZE()  
    {  
        return MAX_SIZE;  
    }  
 
    public LinkedList<Object> getList()  
    {  
        return list;  
    }  
 
    public void setList(LinkedList<Object> list)  
    {  

測試類

測試類算是用用,我們直接使用多線程調(diào)度器Executor來做。對于生產(chǎn)者消費者使用隨機數(shù)的方式來初始化物品數(shù),倉庫使用單例模式。

package com.jeanron.main;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.jeanron.entity1.Consumer;
import com.jeanron.entity1.Producer;
import com.jeanron.test1.Storage;

 
public class Test1  
{  
    public static void main(String[] args)  
    {  
        
        ExecutorService exec = Executors.newCachedThreadPool();
        for(int i=0;i<5;i++){
            exec.execute(new Producer(Storage.getInstance(),new Random().nextInt(10)*10));            
        }
        
        for(int i=0;i<10;i++){
            exec.execute(new Consumer(Storage.getInstance(),new Random().nextInt(10)*10));            
        }
        
    }  

測試結(jié)果

生產(chǎn)者是5個線程,消費者是10個線程,我們來看看調(diào)用之后的信息,這個可以簡單分析下日志即可,不具有典型性和代表性。

第一類實現(xiàn)方式的輸出如下:

pool-1-thread-1【生產(chǎn)者:已經(jīng)生產(chǎn)產(chǎn)品數(shù)】:10    【現(xiàn)倉儲量為】:10
pool-1-thread-3【生產(chǎn)者:已經(jīng)生產(chǎn)產(chǎn)品數(shù)】:50    【現(xiàn)倉儲量為】:60
pool-1-thread-2【生產(chǎn)者:已經(jīng)生產(chǎn)產(chǎn)品數(shù)】:50    【現(xiàn)倉儲量為】:110
pool-1-thread-5【生產(chǎn)者:已經(jīng)生產(chǎn)產(chǎn)品數(shù)】:0    【現(xiàn)倉儲量為】:110
pool-1-thread-4【生產(chǎn)者:已經(jīng)生產(chǎn)產(chǎn)品數(shù)】:80    【現(xiàn)倉儲量為】:190
pool-1-thread-4【消費者:已經(jīng)消費產(chǎn)品數(shù)】:40    【現(xiàn)倉儲量為】:150
pool-1-thread-1【消費者:已經(jīng)消費產(chǎn)品數(shù)】:10    【現(xiàn)倉儲量為】:140
pool-1-thread-3【消費者:已經(jīng)消費產(chǎn)品數(shù)】:20    【現(xiàn)倉儲量為】:120
pool-1-thread-2【消費者:已經(jīng)消費產(chǎn)品數(shù)】:10    【現(xiàn)倉儲量為】:110
pool-1-thread-5【消費者:已經(jīng)消費產(chǎn)品數(shù)】:40    【現(xiàn)倉儲量為】:70
pool-1-thread-6【消費者:已經(jīng)消費產(chǎn)品數(shù)】:30    【現(xiàn)倉儲量為】:40
pool-1-thread-4【消費者:要消費的產(chǎn)品數(shù)量】:90    【庫存量】:40    暫時不能執(zhí)行消費任務!
pool-1-thread-1【消費者:要消費的產(chǎn)品數(shù)量】:50    【庫存量】:40    暫時不能執(zhí)行消費任務!
pool-1-thread-3【消費者:要消費的產(chǎn)品數(shù)量】:90    【庫存量】:40    暫時不能執(zhí)行消費任務!
pool-1-thread-7【消費者:已經(jīng)消費產(chǎn)品數(shù)】:20    【現(xiàn)倉儲量為】:20
pool-1-thread-3【消費者:要消費的產(chǎn)品數(shù)量】:90    【庫存量】:20    暫時不能執(zhí)行消費任務!
pool-1-thread-1【消費者:要消費的產(chǎn)品數(shù)量】:50    【庫存量】:20    暫時不能執(zhí)行消費任務!
pool-1-thread-4【消費者:要消費的產(chǎn)品數(shù)量】:90    【庫存量】:20    暫時不能執(zhí)行消費任務!

第二類實現(xiàn)方式的輸出如下:

pool-1-thread-1【已經(jīng)生產(chǎn)產(chǎn)品數(shù)】:16    【現(xiàn)倉儲量為】:16
pool-1-thread-3【已經(jīng)生產(chǎn)產(chǎn)品數(shù)】:60    【現(xiàn)倉儲量為】:76
pool-1-thread-2【已經(jīng)生產(chǎn)產(chǎn)品數(shù)】:67    【現(xiàn)倉儲量為】:143
pool-1-thread-5【要生產(chǎn)的產(chǎn)品數(shù)量】:90    【庫存量】:143    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-4【已經(jīng)生產(chǎn)產(chǎn)品數(shù)】:27    【現(xiàn)倉儲量為】:170
pool-1-thread-1【已經(jīng)消費產(chǎn)品數(shù)】:10    【現(xiàn)倉儲量為】:160
pool-1-thread-2【已經(jīng)消費產(chǎn)品數(shù)】:23    【現(xiàn)倉儲量為】:137
pool-1-thread-6【已經(jīng)消費產(chǎn)品數(shù)】:18    【現(xiàn)倉儲量為】:119
pool-1-thread-5【要生產(chǎn)的產(chǎn)品數(shù)量】:90    【庫存量】:119    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-3【已經(jīng)消費產(chǎn)品數(shù)】:97    【現(xiàn)倉儲量為】:22
pool-1-thread-1【要消費的產(chǎn)品數(shù)量】:57    【庫存量】:22    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-6【要消費的產(chǎn)品數(shù)量】:62    【庫存量】:22    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-3【要消費的產(chǎn)品數(shù)量】:35    【庫存量】:22    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-4【已經(jīng)消費產(chǎn)品數(shù)】:17    【現(xiàn)倉儲量為】:5
pool-1-thread-2【已經(jīng)消費產(chǎn)品數(shù)】:1    【現(xiàn)倉儲量為】:4
pool-1-thread-7【要消費的產(chǎn)品數(shù)量】:93    【庫存量】:4    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-2【已經(jīng)消費產(chǎn)品數(shù)】:4    【現(xiàn)倉儲量為】:0
pool-1-thread-5【已經(jīng)生產(chǎn)產(chǎn)品數(shù)】:90    【現(xiàn)倉儲量為】:90
pool-1-thread-1【已經(jīng)消費產(chǎn)品數(shù)】:57    【現(xiàn)倉儲量為】:33
pool-1-thread-6【要消費的產(chǎn)品數(shù)量】:62    【庫存量】:33    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-3【要消費的產(chǎn)品數(shù)量】:35    【庫存量】:33    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-4【要消費的產(chǎn)品數(shù)量】:45    【庫存量】:33    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-7【要消費的產(chǎn)品數(shù)量】:93    【庫存量】:33    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-10【要消費的產(chǎn)品數(shù)量】:81    【庫存量】:33    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-2【要消費的產(chǎn)品數(shù)量】:65    【庫存量】:33    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-5【要消費的產(chǎn)品數(shù)量】:38    【庫存量】:33    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-8【已經(jīng)消費產(chǎn)品數(shù)】:33    【現(xiàn)倉儲量為】:0
pool-1-thread-12【要消費的產(chǎn)品數(shù)量】:21    【庫存量】:0    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-9【要消費的產(chǎn)品數(shù)量】:24    【庫存量】:0    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-11【要消費的產(chǎn)品數(shù)量】:54    【庫存量】:0    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-13【要消費的產(chǎn)品數(shù)量】:58    【庫存量】:0    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-6【要消費的產(chǎn)品數(shù)量】:62    【庫存量】:0    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-3【要消費的產(chǎn)品數(shù)量】:35    【庫存量】:0    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-4【要消費的產(chǎn)品數(shù)量】:45    【庫存量】:0    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-7【要消費的產(chǎn)品數(shù)量】:93    【庫存量】:0    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-10【要消費的產(chǎn)品數(shù)量】:81    【庫存量】:0    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-2【要消費的產(chǎn)品數(shù)量】:65    【庫存量】:0    暫時不能執(zhí)行生產(chǎn)任務!
pool-1-thread-5【要消費的產(chǎn)品數(shù)量】:38    【庫存量】:0    暫時不能執(zhí)行生產(chǎn)任務!

以上就是Java實現(xiàn)生產(chǎn)者消費者的兩種方式分別是什么,小編相信有部分知識點可能是我們?nèi)粘9ぷ鲿姷交蛴玫降摹OM隳芡ㄟ^這篇文章學到更多知識。更多詳情敬請關注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI