簡體   English   中英

何時以及如何使用wait()和notify()

[英]When and How to use wait() and notify()

我從SO找到了這個例子。 現在,我試圖了解wait()notify()/notifyAll()的用法。 在哪種情況下以及為什么需要這樣做。

class BlockingQueue<T> {

    private Queue<T> queue = new LinkedList<T>();
    private int capacity;

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    public synchronized void put(T element) throws InterruptedException {
        while (queue.size() == capacity) {
             System.out.println("Waiting...");
            wait();

        }

        queue.add(element);
        notify(); // notifyAll() for multiple producer/consumer threads
    }

    public synchronized T take() throws InterruptedException {
        while (queue.isEmpty()) {
            wait();
        }

        T item = queue.remove();
        notify(); // notifyAll() for multiple producer/consumer threads
        return item;
    }
}

因此,實現了Runnable和重寫的run()方法,如下所示

 @Override
    public void run() {
        // synchronized (this) {
        BlockingQueue<Integer> s = new BlockingQueue(10);
        for (int i = 0; i < 12; i++) {
            try {
                s.put(i);
                if (i > 9) {
                    System.out.println(Thread.currentThread().getName() + "  : " + s.take());
                }
                System.out.println(Thread.currentThread().getName() + " ExtendsThread : Counter : " + i);
            } //}
            //notify();
            catch (InterruptedException ex) {
                Logger.getLogger(ExtendsThread.class.getName()).log(Level.SEVERE, null, ex);
            }

        }

    }

並且,如下運行線程

 ImplementsRunnable rc = new ImplementsRunnable();
        Thread t1 = new Thread(rc, "A");
        t1.start();

當我運行它時,它在counter : 9之后卡住,並一直等待着永遠。 有人建議我這是怎么了?

您的概念有些瑕疵。 BlockingQueue可以充當生產者/消費者模式中的橋梁。

也就是說,它允許一個線程向其中寫入內容,而另一個線程從中讀取內容,但是這樣做的前提是:

  • 沒有要取的物品,它會等到新物品到達
  • 如果物品太多,它將等待物品被移除

在這種情況下, waitnotifyBlockingQueue實例的內部消息傳遞

您可以查看“ 固有鎖定和同步”

因此,您應該使用(至少)兩個,而不是僅使用一個線程,一個農產品和一個消費者...

制片人

這需要一個BlockingQueue實例,並向其中添加int值。 每次停止1秒鍾,然后添加下一個

public class Producer implements Runnable {

    private BlockingQueue<Integer> queue;

    public Producer(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        for (int index = 0; index < 10; index++) {
            try {
                System.out.println("Put " + index);
                queue.put(index);
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
            }
        }
    }

}

消費者

使用者使用BlockQueue並從中讀取int值,該值將被阻塞直到值存在。

public class Consumer implements Runnable {

    private BlockingQueue<Integer> queue;

    public Consumer(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            while (true) {
                Integer value = queue.take();
                System.out.println("Took " + value);
            }
        } catch (InterruptedException ex) {
            Logger.getLogger(JavaApplication220.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

}

您可以使用類似...

BlockingQueue bq = new BlockingQueue(10);
Thread p = new Thread(new Producer(bq));
Thread c = new Thread(new Consumer(bq));
c.setDaemon(true);
c.start();
p.start();

您應該注意, put消息之間的延遲很小,但是took消息之間幾乎沒有延遲。 這是正在執行的隊列。 Consumer正在阻止/等待隊列中的東西給它。

您可以與“ Producer和“ Consumer一起玩耍,也許可以改變他們的時間(例如,在拿一件物品之前,“ Consumer會有更長的延遲),以了解這可能會帶來怎樣的不同影響

當我運行它時,它在計數器9之后停滯不前,並一直等待着永遠

這可能是因為您已經超出了隊列的容量,並且put方法一直處於阻塞狀態,直到您從隊列中取出某些東西為止(您實際上有一個死鎖,隊列正在等待您從中取出某些東西,但是您可以計算負擔,因為你鎖定在put

要記住的事情:

  • 為了使兩個或多個線程可以使用監視器鎖,它們必須共享相同的監視器/對象鎖實例。 在這種情況下, BlockingQueue的相同實例
  • notify將喚醒一個在監視器鎖的wait方法的同一實例上等待的對象。 沒有辦法知道哪一個。 如果您有多個使用者,但不關心數據的處理順序,例如,這可能很有用。

更新了其他示例

因此,這將Thread.sleepProducer Thread.sleep (並允許生產者產生100個值),並將Thread.sleep添加到Consumer

這樣, Producer將在Consumer耗盡其容量之前達到其容量,迫使其等待直到Consumer可以從中獲取價值為止。

public class Producer implements Runnable {

    private BlockingQueue<Integer> queue;

    public Producer(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        for (int index = 0; index < 100; index++) {
            try {
                System.out.println("Put " + index);
                queue.put(index);
            } catch (InterruptedException ex) {
            }
        }
    }

}

public class Consumer implements Runnable {

    private BlockingQueue<Integer> queue;

    public Consumer(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            while (true) {
                Integer value = queue.take();
                System.out.println("Took " + value);
                Thread.sleep(1000);
            }
        } catch (InterruptedException ex) {
        }
    }

}

在此處添加printlns

public synchronized void put(T element) throws InterruptedException {
    while (queue.size() == capacity) {
        System.out.println("blocked");
        wait();
    }
    queue.add(element);
    notify(); // notifyAll() for multiple producer/consumer threads
    System.out.println("put "+ element);
}

public synchronized T take() throws InterruptedException {
    while (queue.isEmpty()) {
        wait();
    }
    T item = queue.remove();
    notify(); // notifyAll() for multiple producer/consumer threads
    System.out.println("removed " + item);
    return item;
}

並運行此測試

public static void main(String argv[]) throws Exception {
    final BlockingQueue q = new BlockingQueue(2);
    new Thread() {
        public void run() {
            try {
                Thread.sleep(5000);
                q.take();
            } catch (Exception e) {
                e.printStackTrace();
            }
        };
    }.start();
    q.put(1);
    q.put(2);  // will block here until tread 2 takes an element and reduces the capacity
    q.put(3);   
}

它將打印

put 1
put 2
blocked
removed 1
put 3

暫無
暫無

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

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