JAVA多線程之wait/notify

概述

本文主要學習JAVA多線程中的 wait()方法 與 notify()/notifyAll()方法的用法。html

目錄以下:java

  • ①wait() 與 notify/notifyAll 方法必須在同步代碼塊中使用
  • ②wait() 與 notify/notifyAll() 的執行過程
  • ③中斷 調用wait()方法進入等待隊列的 線程
  • ④notify 通知的順序不能錯
  • ⑤多線程中測試某個條件的變化用 if 仍是用 while?

①wait() 與 notify/notifyAll 方法必須在同步代碼塊中使用

wait() 與 notify/notifyAll() 是Object類的方法,在執行兩個方法時,要先得到鎖。那麼怎麼得到鎖呢?編程

JAVA多線程之Synchronized關鍵字--對象鎖的特色。文章中介紹了使用synchronized關鍵字得到鎖。所以,wait() 與 notify/notifyAll() 常常與synchronized搭配使用,即在synchronized修飾的同步代碼塊或方法裏面調用wait() 與 notify/notifyAll()方法。多線程

②wait() 與 notify/notifyAll() 的執行過程

因爲 wait() 與 notify/notifyAll() 是放在同步代碼塊中的,所以線程在執行它們時,確定是進入了臨界區中的,即該線程確定是得到了鎖的。ide

當線程執行wait()時,會把當前的鎖釋放,而後讓出CPU,進入等待狀態。學習

當執行notify/notifyAll方法時,會喚醒一個處於等待該 對象鎖 的線程,而後繼續往下執行,直到執行完退出對象鎖鎖住的區域(synchronized修飾的代碼塊)後再釋放鎖。測試

從這裏能夠看出,notify/notifyAll()執行後,並不當即釋放鎖,而是要等到執行完臨界區中代碼後,再釋放。故,在實際編程中,咱們應該儘可能在線程調用notify/notifyAll()後,當即退出臨界區。即不要在notify/notifyAll()後面再寫一些耗時的代碼。示例以下:this

public class Service {

    public void testMethod(Object lock) {
        try {
            synchronized (lock) {
                System.out.println("begin wait() ThreadName="
                        + Thread.currentThread().getName());
                lock.wait();
                System.out.println("  end wait() ThreadName="
                        + Thread.currentThread().getName());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void synNotifyMethod(Object lock) {
        try {
            synchronized (lock) {
                System.out.println("begin notify() ThreadName="
                        + Thread.currentThread().getName() + " time="
                        + System.currentTimeMillis());
                lock.notify();
                Thread.sleep(5000);
                System.out.println("  end notify() ThreadName="
                        + Thread.currentThread().getName() + " time="
                        + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在第3行的testMethod()中調用 wait(),在第17行的synNotifyMethod()中調用notify()線程

從上面的代碼能夠看出,wait() 與 notify/notifyAll()都是放在同步代碼塊中才可以執行的。若是在執行wait() 與 notify/notifyAll() 以前沒有得到相應的對象鎖,就會拋出:java.lang.IllegalMonitorStateException異常。code

在第8行,當ThreadA線程執行lock.wait();這條語句時,釋放得到的對象鎖lock,並放棄CPU,進入等待隊列。

當另外一個線程執行第23行lock.notify();,會喚醒ThreadA,可是此時它並不當即釋放鎖,接下來它睡眠了5秒鐘(sleep()是不釋放鎖的,事實上sleep()也能夠不在同步代碼塊中調用),直到第28行,退出synchronized修飾的臨界區時,纔會把鎖釋放。這時,ThreadA就有機會得到另外一個線程釋放的鎖,並從等待的地方起(第9行)起開始執行。

接下來是兩個線程類,線程類ThreadA調用testMethod()方法執行lock.wait();時被掛起,另外一個線程類synNotifyMethodThread調用synNotifyMethod()負責喚醒掛起的線程。代碼以下:

public class ThreadA extends Thread {
    private Object lock;

    public ThreadA(Object lock) {
        super();
        this.lock = lock;
    }

    @Override
    public void run() {
        Service service = new Service();
        service.testMethod(lock);
    }
}

public class SynNotifyMethodThread extends Thread {
    private Object lock;

    public SynNotifyMethodThread(Object lock) {
        super();
        this.lock = lock;
    }

    @Override
    public void run() {
        Service service = new Service();
        service.synNotifyMethod(lock);
    }
}

再接下來是測試類:

public class Test {

    public static void main(String[] args) throws InterruptedException {

        Object lock = new Object();

        ThreadA a = new ThreadA(lock);
        a.start();

        //NotifyThread notifyThread = new NotifyThread(lock);
       // notifyThread.start();

        SynNotifyMethodThread c = new SynNotifyMethodThread(lock);
        c.start();
    }
}

③中斷 調用wait()方法進入等待隊列的 線程

示例代碼以下:

public class Service {

    public void testMethod(Object lock) {
        try {
            synchronized (lock) {
                System.out.println("begin wait()");
                lock.wait();
                System.out.println("  end wait()");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("出現異常");
        }
    }
}

public class ThreadA extends Thread {

    private Object lock;

    public ThreadA(Object lock) {
        super();
        this.lock = lock;
    }

    @Override
    public void run() {
        Service service = new Service();
        service.testMethod(lock);
    }
}

注意,在第23行wait()方法是Object類的對象lock調用的。而下面的interrupt()方法是ThreadA類的對象調用的。在ThreadA裏面,將Object的對象做爲參數傳給了testMethod()方法,ThreadA的run()方法去調用testMethod(),從而wait()使ThreadA的線程暫停了(暫停當前執行wait()的線程)。從這裏能夠看出一個區別:

Object類中與線程有關的方法:

1)notify/notifyAll

2)wait()/wait(long)

java.lang.Thread中與之相關的方法:

1)interrupt()

2)sleep()/sleep(long)

3)join()/suspend()/resume()....

測試類代碼以下:

public class Test {

    public static void main(String[] args) {

        try {
            Object lock = new Object();

            ThreadA a = new ThreadA(lock);
            a.start();

            Thread.sleep(5000);

            a.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

當執行第13行的interrupt()時,處於wait中的線程「當即」被喚醒(通常是當即響應中斷請求),並拋出異常。此時,線程也就結束了。

④notify 通知的順序不能錯

假設在線程A中執行wait(),在線程B中執行notify()。但若是線程B先執行了notify()而後結束了,線程A纔去執行wait(),那此時,線程A將沒法被正常喚醒了(還能夠經過③中提到的interrupt()方法以拋出異常的方式喚醒^~^)。

這篇文章: JAVA多線程之線程間的通訊方式中的第③點提到了notify通知順序出錯會致使 調用wait()進入等待隊列的線程再也沒法被喚醒了。

⑤多線程中測試某個條件的變化用 if 仍是用 while?

有兩個線程從List中刪除數據,而只有一個線程向List中添加數據。初始時,List爲空,只有往List中添加了數據以後,才能刪除List中的數據。添加數據的線程向List添加完數據後,調用notifyAll(),喚醒了兩個刪除線程,可是它只添加了一個數據,而如今有兩個喚醒的刪除線程,這時怎麼辦??

若是用 if 測試List中的數據的個數,則會出現IndexOutofBoundException,越界異常。緣由是,List中只有一個數據,第一個刪除線程把數據刪除後,第二個線程再去執行刪除操做時,刪除失敗,從而拋出 IndexOutofBoundException。

可是若是用while 測試List中數據的個數,則不會出現越界異常!!!神奇。

當wait等待的條件發生變化時,會形成程序的邏輯混亂---即,List中沒有數據了,再仍是有線程去執行刪除數據的操做。所以,須要用while循環來判斷條件的變化,而不是用if。

示例以下:Add類,負責添加數據:

public class Add {

    private String lock;
    public Add(String lock) {
        super();
        this.lock = lock;
    }

    public void add() {
        synchronized (lock) {
            ValueObject.list.add("anyString");
            lock.notifyAll();
        }
    }
}

public class ThreadAdd extends Thread {

    private Add p;

    public ThreadAdd(Add p) {
        super();
        this.p = p;
    }

    @Override
    public void run() {
        p.add();
    }
}

Subtract類,負責刪除數據----先要進行條件判斷,而後執行wait(),這意味着:wait等待的條件可能發生變化!!!

public class Subtract {

    private String lock;

    public Subtract(String lock) {
        super();
        this.lock = lock;
    }

    public void subtract() {
        try {
            synchronized (lock) {
                if(ValueObject.list.size() == 0) {//將這裏的if改爲while便可保證不出現越界異常!!!!
                    System.out.println("wait begin ThreadName="
                            + Thread.currentThread().getName());
                    lock.wait();
                    System.out.println("wait   end ThreadName="
                            + Thread.currentThread().getName());
                }
                ValueObject.list.remove(0);
                System.out.println("list size=" + ValueObject.list.size());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ThreadSubtract extends Thread {

    private Subtract r;

    public ThreadSubtract(Subtract r) {
        super();
        this.r = r;
    }

    @Override
    public void run() {
        r.subtract();
    }
}

封裝的List隊列:

public class ValueObject {

    public static List list = new ArrayList();

}

測試類:

public class Run {

    public static void main(String[] args) throws InterruptedException {

        String lock = new String("");

        Add add = new Add(lock);
        Subtract subtract = new Subtract(lock);

        ThreadSubtract subtract1Thread = new ThreadSubtract(subtract);
        subtract1Thread.setName("subtract1Thread");
        subtract1Thread.start();

        ThreadSubtract subtract2Thread = new ThreadSubtract(subtract);
        subtract2Thread.setName("subtract2Thread");
        subtract2Thread.start();

        Thread.sleep(1000);

        ThreadAdd addThread = new ThreadAdd(add);
        addThread.setName("addThread");
        addThread.start();

    }
}
相關文章
相關標籤/搜索