簡體   English   中英

使用列表鎖定實現生產者消費者問題

[英]Implementing Producer Consumer problem using lock on list

這是作業的生產者消費者模式的一種實現。 下面的實現有什么問題。 這會導致死鎖。 我無法理解出了什么問題。

我有一個共享隊列

我在同一鎖上同步生產者和消費者

private static volatile Queue<Integer> BUFFER = new LinkedList<>();
private static int COUNT = 0;
private static final int SIZE = 1;

public static void main(String[] args) {

    new Thread(() -> {
        while (true) {
            while (BUFFER.size() == SIZE) {
                synchronized (BUFFER) {
                    try {
                        System.out.println("Producer waiting");
                        BUFFER.wait();
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }

            synchronized (BUFFER) {
                System.out.println("Producer added : " + COUNT);
                BUFFER.offer(COUNT++);
                System.out.println("Producer notify");
                BUFFER.notify();
            }

            try {
                Thread.sleep(500l);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }).start();

    new Thread(() -> {
        while (true) {
            while (BUFFER.isEmpty()) {
                synchronized (BUFFER) {
                    try {
                        System.out.println("Consumer waiting");
                        BUFFER.wait();
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }

            synchronized (BUFFER) {
                System.out.println("Consumer consumed : "+ BUFFER.poll());
                BUFFER.notify();
            }
            try {
                Thread.sleep(500l);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }).start();

}

}

一段時間后,此代碼將導致死鎖。

Producer added : 0
Producer notify
Consumer consumed : 0
Consumer waiting
Producer added : 1
Producer notify
Consumer consumed : 1
Producer added : 2
Producer notify
Consumer waiting
Producer waiting

您的代碼容易丟失通知 這是可能發生的情況:

  1. 使用者檢查緩沖區是否為空,並且是否空。
  2. 生產者將一個項目放入緩沖區並調用BUFFER.notify()
  3. 使用者調用BUFFER.wait() (因為緩沖區在檢查時為空)。
  4. 生產者檢查緩沖區是否已滿( BUFFER.size() == 1 )。
  5. 生產者調用BUFFER.wait()

現在,每個線程都在等待對方做某事:死鎖!

步驟2中的通知丟失。 如果另一個線程尚未等待,則BUFFER.notify()根本不執行任何操作。


實際上,只有一種正確的方法可以使用wait()notify() 您可以在這里閱讀有關內容: https : //docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

為生產者消費者使用notify不是正確的方法。 如果在調用notify()時實際上沒有人在等待該通知,則notify()將丟失。

而不是使用wait()notify() ,應該使用semaphore

共享semaphore可以由任何線程釋放和獲取,並且是在這種類型的線程間通信中使用的正確工具。

在這種情況下,使用者將從隊列中消費並release() semaphore 生產者將acquire()信號量並將某些內容推入隊列。 如果隊列已滿,則生產者將在嘗試獲取semaphore直到消費者清空隊列。 與您基本上在這里使用的條件變量不同,通過使用wait()notify()semaphore保持狀態。

在Udemy的“ Java多線程,並發和性能優化 ”課程中,所有有關綁定隊列和未綁定隊列的示例都進行了深入討論。

它說明了何時使用條件變量以及何時以及如何使用信號量。

希望對您有所幫助

下面的實現有什么問題。 這會導致死鎖。 我無法理解出了什么問題。

while (BUFFER.isEmpty()) {
    synchronized (BUFFER) {

你的代碼是接近,但synchronized關鍵字必須包括在這里while循環為它工作,否則由@SolomonSlow所列舉它將從競爭條件受到影響。 這不是並發集合,盡管您將其標記為volatile但不能保證isEmpty()方法將返回true,可能會向列表中添加元素,然后使用者進入synchronized塊。 您需要確保測試和等待發生在同一鎖中。

一旦你移動synchronized關鍵字,可以去除volatile則由於所有訪問BUFFER是內部synchronized

synchronized (BUFFER) {
    while (BUFFER.size() == SIZE) {
...
synchronized (BUFFER) {
    while (BUFFER.isEmpty()) {

通過將同步移到兩個同時,您的代碼似乎對我有用。

其他一些評論:

  • 您可以將System.out.println()notify()調用包含在原始synchronized 那里不需要2個街區。
  • 注意不要離開e.printStackTrace(); 和TODO。
  • 您不應該捕獲Exception而應使其盡可能地精細。 catch InterruptedException
  • 捕獲InterruptedException ,應根據策略始終重新中斷線程。

     } catch (InterruptedException ie) { Thread.currentThread().interrupt(); // now handle the interrupt by quitting the thread or something } 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM