Java Thread wait、notify與notifyAll

Java的Object類包含了三個final方法,容許線程就資源的鎖定狀態進行通訊。這三個方法分別是:wait(),notify(),notifyAll(),今天來了解一下這三個方法。在任何對象上調用這些方法的當前線程應具備對象監視器(鎖住了一個對象,就是得到對象相關聯的監視器),不然會拋出java.lang.IllegalMonitorStateException異常。java

wait

Object.wait有三種重載的實現,一個無限期等待任何其餘線程地調用對象的notify或notifyAll方法來喚醒當前線程。 其餘兩個會使當前線程在等待特定的時間後進行喚醒。app

wait()

使得當前線程進程等待,直到另外一個線程在這個對象上調用了notify()方法或者notifyAll()方法。這個方法的行爲,徹底等價於調用wait(0),能夠看它的實現代碼爲:ide

 

    public final void wait() throws InterruptedException {
        wait(0);
    }

 

當前的線程必須得到這個對象的監視器。線程釋放了監視器的全部權,直到另外一個線程調用notify方法或者notifyAll方法,去喚醒在這個對象監視器上等待的其餘線程。釋放了監視器的全部權後,線程便進行等待,直到從新得到監視器的全部權並恢復執行。處於wait狀態中的線程,可能被中斷或虛假喚醒,因此這個方法應該老是在一個循環中使用:測試

         synchronized (obj) {
              while (<condition does not hold>)
                  obj.wait();
              ... // Perform action appropriate to condition
          }

虛假喚醒指的是一些obj.wait()會在除了obj.notify()和obj.notifyAll()的其餘狀況被喚醒,而此時是不該該喚醒的,更詳細的能夠看Spurious_wakeup
this

方法會拋出兩種異常:spa

IllegalMonitorStateException:若是當前線程沒有得到當前對象的監視器。操作系統

InterruptedException:若是某個線程在當前線程等待通知的時候,或是在等待通知以前中斷了當前線程,當拋出這個異常時,當前線程的中斷狀態被清除。線程

wait(long timeout)

該方法會使得當前進程進入wait狀態直到另外一個線程調用notify/notifyAll,或是通過了指定的時間,線程將被喚醒。該方法會拋出異常:code

IllegalArgumentException:若是timeout是負數。orm

wait(long timeout, int nanos)

一樣的,該方法會使得當前進程進入wait狀態直到另外一個線程調用notify/notifyAll。它能夠更加精細地控制等待的時間,以納秒爲單位測量的實時量由下式給出:1000000*timeout+nanos。除此以外,這個方法與wait(long)作相同的事情,特別的,wait(0,0)等價於wait(0)。除了其他兩個wait方法會拋出的異常外,這個方法會拋出異常:

IllegalArgumentException:若是timeout是負數,或者nanos的範圍不在0-999999之間時,拋出該異常。

notify

notify方法只喚醒等待對象的一個線程,而且該線程開始執行。因此若是有多個線程在等待一個對象,這個方法只會喚醒其中的一個。線程的選擇取決於線程管理的OS實現。

notifyAll

notifyAll方法喚醒等待對象的全部線程,但哪個將首先處理取決於操做系統的實現。

這些方法可用於實現生產者消費者問題,其中消費者線程正在等待隊列中的對象,生產者線程將對象放入隊列中並通知等待的線程。下面是一個多個線程工做在同一個對象上的例子,使用了wait,notify,notifyAll方法:

 一個例子

Message :被通知喚醒的對象

public class Message {
    private String msg;

    public Message(String str){
        this.msg=str;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String str) {
        this.msg=str;
    }

}

Notifier:執行喚醒操做

public class Notifier implements Runnable {

    private Message msg;

    public Notifier(Message msg) {
        this.msg = msg;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name+" started");
        try {
            Thread.sleep(1000);
            synchronized (msg) {
                msg.setMsg(name+" Notifier work done");
                msg.notify();
//                 msg.notifyAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

Waiter: 使Message 對象進行wait狀態

public class Waiter implements Runnable{

    private Message msg;

    public Waiter(Message m){
        this.msg=m;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        synchronized (msg) {
            try{
                System.out.println(name+" waiting to get notified at time:"+System.currentTimeMillis());
                msg.wait();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(name+" waiter thread got notified at time:"+System.currentTimeMillis());
            //process the message now
            System.out.println(name+" processed: "+msg.getMsg());
        }
    }

}

WaitNotifyTest:測試

public class WaitNotifyTest {

    public static void main(String[] args) {
        Message msg = new Message("process it");
        Waiter waiter = new Waiter(msg);
        new Thread(waiter,"waiter").start();

        Waiter waiter1 = new Waiter(msg);
        new Thread(waiter1, "waiter1").start();

        Notifier notifier = new Notifier(msg);
        new Thread(notifier, "notifier").start();
        System.out.println("All the threads are started");
    }

}

輸出:

waiter waiting to get notified at time:1516757290631
All the threads are started
waiter1 waiting to get notified at time:1516757290632
notifier started
waiter waiter thread got notified at time:1516757291632
waiter processed: notifier Notifier work done

能夠看到兩個線程在對象msg上進行等待,調用notify方法時,只有一個線程被喚醒,此時程序並無退出,由於還有一個線程在等待。

若是把notify方法改爲notifyAll,運行結果爲:

waiter waiting to get notified at time:1516757437164
waiter1 waiting to get notified at time:1516757437164
All the threads are started
notifier started
waiter1 waiter thread got notified at time:1516757438165
waiter1 processed: notifier Notifier work done
waiter waiter thread got notified at time:1516757438165
waiter processed: notifier Notifier work done

能夠看到兩個線程都被喚醒了,程序也退出運行了。

(完)

相關文章
相關標籤/搜索