1. 程式人生 > >Java—wait 方法和notify方法

Java—wait 方法和notify方法

1. wait()—痴漢方法

wait()方法就是使執行緒停止執行,會釋放物件鎖。

wait()方法是從執行態回阻塞態。
notifi( ) 方法是從阻塞態回執行態。

  1. wait()方法會使當前執行緒呼叫該方法後進行等待,並且將該執行緒置入鎖物件的等待佇列中,直到接到通知或被中斷為止。
  2. wait()方法只能在同步方法或同步程式碼塊中呼叫,如果呼叫wait()時沒有適當的鎖,會丟擲異常。
  3. wait()方法執行後,當前執行緒釋放鎖,其他執行緒可以競爭該鎖。
  • wait()之後的執行緒繼續執行有兩種方法:
  1. 呼叫該物件的notify()方法喚醒等待執行緒。
  2. 執行緒等待時呼叫interrupt()中斷該執行緒。

wait(long time) :如果到了預計時間還未被喚醒,執行緒將繼續執行。

class MyThread implements Runnable{
    private Object object;
    private boolean flag;

    public MyThread(Object object, boolean flag) {
        this.object = object;
        this.flag = flag;
    }

    public void waitMethod() {
        synchronized (object) {
            System.out.println("wait方法開始..." + Thread.currentThread().getName());
            try {
                object.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("wait方法結束..." + Thread.currentThread().getName());
        }
    }
    public void notifyMethod(){
        synchronized (object) {
            System.out.println("notify方法開始..." + Thread.currentThread().getName());
            object.notify();
            System.out.println("notify方法結束..." + Thread.currentThread().getName());
        }
    }
    public void run(){
        if(flag){
           this.waitMethod();
        }else{
           this.notifyMethod();
        }
    }
}
public class Object {
    public static void main(String[] args)throws InterruptedException{
        Object obj = new Object();
        MyThread thread1 = new MyThread(obj,true);
        MyThread thread2 = new MyThread(obj,false);
        Thread waitThread = new Thread(thread1,"等待執行緒");
        Thread notifyThread = new Thread(thread2,"喚醒執行緒");
        waitThread.start();
        Thread.sleep(1000);
        notifyThread.start();
    }
}

執行結果:
wait方法開始...等待執行緒
notify方法開始...喚醒執行緒
notify方法結束...喚醒執行緒
wait方法結束...等待執行緒

2. notify()方法

  1. notify()方法必須在同步方法或同步程式碼塊中呼叫,用來喚醒等待在該物件上的執行緒,如果有多個執行緒等待,則任意挑選一個執行緒喚醒。
  2. notify()方法執行後,喚醒執行緒不會立刻釋放鎖,要等喚醒執行緒全部執行完畢後才釋放物件鎖。
class MyThread implements Runnable{
    private Object object;
    private boolean flag;

    public MyThread(Object object, boolean flag) {
        this.object = object;
        this.flag = flag;
    }

    public void waitMethod() {
        synchronized (object) {
            System.out.println("wait方法開始..." + Thread.currentThread().getName());
            try {
                object.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("wait方法結束..." + Thread.currentThread().getName());
        }
    }
    public void notifyMethod(){
        synchronized (object) {
            System.out.println("notify方法開始..." + Thread.currentThread().getName());
            object.notify();
            System.out.println("notify方法結束..." + Thread.currentThread().getName());
        }
    }
    public void run(){
        if(flag){
           this.waitMethod();
        }else{
           this.notifyMethod();
        }
    }
}
public class Object {
    public static void main(String[] args)throws InterruptedException{
        Object obj = new Object();
        MyThread thread1 = new MyThread(obj,true);
        MyThread thread2 = new MyThread(obj,false);
        for(int i = 0;i<5;i++) {
            Thread threadi = new Thread(thread1, "等待執行緒"+i);
            threadi.start();
        }
        Thread notifyThread = new Thread(thread2,"喚醒執行緒");
        Thread.sleep(1000);
        notifyThread.start();
    }
}
wait方法開始...等待執行緒0
wait方法開始...等待執行緒1
wait方法開始...等待執行緒2
wait方法開始...等待執行緒3
wait方法開始...等待執行緒4
notify方法開始...喚醒執行緒
notify方法結束...喚醒執行緒
wait方法結束...等待執行緒0
//當有多個執行緒同時等待時,notify方法任意挑選一個喚醒。

wait()和notify()均用於同步方法或同步程式碼塊並且必須是內建鎖。

3. notifyAll()

喚醒所有在該物件上等待的執行緒。

class MyThread implements Runnable{
    private Object object;
    private boolean flag;

    public MyThread(Object object, boolean flag) {
        this.object = object;
        this.flag = flag;
    }

    public void waitMethod() {
        synchronized (object) {
            System.out.println("wait方法開始..." + Thread.currentThread().getName());
            try {
                object.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("wait方法結束..." + Thread.currentThread().getName());
        }
    }
    public void notifyMethod(){
        synchronized (object) {
            System.out.println("notify方法開始..." + Thread.currentThread().getName());
            object.notifyAll();
            System.out.println("notify方法結束..." + Thread.currentThread().getName());
        }
    }
    public void run(){
        if(flag){
           this.waitMethod();
        }else{
           this.notifyMethod();
        }
    }
}
public class Object {
    public static void main(String[] args)throws InterruptedException{
        Object obj = new Object();
        MyThread thread1 = new MyThread(obj,true);
        MyThread thread2 = new MyThread(obj,false);
        for(int i = 0;i<5;i++) {
            Thread threadi = new Thread(thread1, "等待執行緒"+i);
            threadi.start();
        }
        Thread notifyThread = new Thread(thread2,"喚醒執行緒");
        Thread.sleep(1000);
        notifyThread.start();
    }
}
執行結果:
wait方法開始...等待執行緒0
wait方法開始...等待執行緒1
wait方法開始...等待執行緒2
wait方法開始...等待執行緒3
wait方法開始...等待執行緒4
notify方法開始...喚醒執行緒
notify方法結束...喚醒執行緒
wait方法結束...等待執行緒4
wait方法結束...等待執行緒3
wait方法結束...等待執行緒2
wait方法結束...等待執行緒1
wait方法結束...等待執行緒0

4.執行緒阻塞

  1. 呼叫sleep()方法,主動放棄佔有的CPU,不會釋放物件鎖。
  2. 呼叫阻塞式IO方法(read()、write()),在該方法返回前,執行緒阻塞。
  3. 執行緒試圖獲取一個monitor,但該monitor被其他執行緒所持有導致阻塞。
  4. 執行緒等待某個通知,即呼叫wait(),釋放物件鎖。
  5. 呼叫執行緒suspend(),將執行緒掛起,容易導致死鎖,已被廢棄。
    這五個執行緒會從執行狀態到阻塞狀態。

5. monitor 的兩個佇列

每個monitor都有兩個佇列,一個稱為同步佇列,一個稱為等待佇列。

  • 同步佇列中存放了因為競爭monitor失敗導致阻塞的執行緒,這些執行緒等待CPU排程再次競爭鎖。
  • 等待佇列存放所有呼叫wait()方法導致執行緒等待的執行緒,喚醒後進入同步佇列競爭鎖。