java 多線程 22 :生產者/消費者模式 進階 利用await()/signal()實現


java多線程15 :wait()和notify() 的生產者/消費者模式
在這一章已經實現了  wait/notify 生產消費模型

利用await()/signal()實現生產者和消費者模型

一樣,先定義一個緩沖區:

public class ValueObject
{
    public static String value = "";
}

換種寫法,生產和消費方法放在一個類里面:

public class ThreadDomain41 extends ReentrantLock
{
    private Condition condition = newCondition();
    
    public void set()
    {
        try
        {
            lock();
            while (!"".equals(ValueObject.value))
                condition.await();
            ValueObject.value = "123";
            System.out.println(Thread.currentThread().getName() + "生產了value, value的當前值是" + ValueObject.value);
            condition.signal();
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        finally
        {
            unlock();
        }
    }
    
    public void get()
    {
        try
        {
            lock();
            while ("".equals(ValueObject.value))
                condition.await();
            ValueObject.value = "";
            System.out.println(Thread.currentThread().getName() + "消費了value, value的當前值是" + ValueObject.value);
            condition.signal();
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        finally
        {
            unlock();
        }
    }
}

同樣的,開兩個線程,一個線程調用set()方法生產,另一個線程調用get()方法消費:

public static void main(String[] args)
{
    final ThreadDomain41 td = new ThreadDomain41();
    Runnable producerRunnable = new Runnable()
    {
        public void run()
        {
            for (int i = 0; i < Integer.MAX_VALUE; i++)
                td.set();
        }
    };
    Runnable customerRunnable = new Runnable()
    {
        public void run()
        {
            for (int i = 0; i < Integer.MAX_VALUE; i++)
                td.get();
        }
    };
    Thread ProducerThread = new Thread(producerRunnable);
    ProducerThread.setName("Producer");
    Thread ConsumerThread = new Thread(customerRunnable);
    ConsumerThread.setName("Consumer");
    ProducerThread.start();
    ConsumerThread.start();
}

看一下運行結果:

...
Producer生產了value, value的當前值是123
Consumer消費了value, value的當前值是
Producer生產了value, value的當前值是123
Consumer消費了value, value的當前值是
Producer生產了value, value的當前值是123
Consumer消費了value, value的當前值是
...

和wait()/notify()機制的實現效果一樣,同樣符合生產者/消費者模型

 

小心假死

生產者/消費者模型最終達到的目的是平衡生產者和消費者的處理能力,達到這個目的的過程中,並不要求只有一個生產者和一個消費者。可以多個生產者對應多個消費者,可以一個生產者對應一個消費者,可以多個生產者對應一個消費者。

假死就發生在上面三種場景下。理論分析就能說明問題,所以就不寫代碼了。代碼要寫也很簡單,上面的兩個例子隨便修改一個,開一個生產者線程/多個消費者線程、開多個生產者線程/消費者線程、開多個生產者線程/多個消費者線程都可以。假死指的是全部線程都進入了WAITING狀態,那么程序就不再執行任何業務功能了,整個項目呈現停滯狀態。

比方說有生產者A和生產者B,緩沖區由於空了,消費者處於WAITING。生產者B處於WAITING,生產者A被消費者通知生產,生產者A生產出來的產品本應該通知消費者,結果通知了生產者B,生產者B被喚醒,發現緩沖區滿了,於是繼續WAITING。至此,兩個生產者線程處於WAITING,消費者處於WAITING,系統假死。

上面的分析可以看出,假死出現的原因是因為notify的是同類,所以非單生產者/單消費者的場景,可以采取兩種方法解決這個問題:

1、synchronized用notifyAll()喚醒所有線程、ReentrantLock用signalAll()喚醒所有線程

2、用ReentrantLock定義兩個Condition,一個表示生產者的Condition,一個表示消費者的Condition,喚醒的時候調用相應的Condition的signal()方法就可以了

 

這里對比 和 wait/notify ,await()/signal() 可以利用多個Condition 進行消費/生產實現效果,不用通知所有線程,這里顯得更加效率,方便

要實現生產消費模型,java提供了隊列機制更加方便的實現,參考  java 多線程阻塞隊列 與 阻塞方法與和非阻塞方法


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM