多線程學習(4)wait/notify

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

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

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

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

④notify 通知的順序不能錯ide

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

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

wait() 與 notify/notifyAll() 是Object類的方法,在執行兩個方法時,要先得到鎖。測試

咱們常常使用synchronized關鍵字得到鎖。所以,wait()與notify/notifyAll()常常與synchronized搭配使用,即在synchronized修飾的同步代碼塊或方法裏面調用wait() 與  notify/notifyAll()方法。this

不在synchronized關鍵字中使用,會拋出異常。spa

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

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

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

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

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

示例以下:

1 public class Service {
 2 
 3     public void testMethod(Object lock) {
 4         try {
 5             synchronized (lock) {
 6                 System.out.println("begin wait() ThreadName="
 7                         + Thread.currentThread().getName());
 8                 lock.wait();
 9                 System.out.println("  end wait() ThreadName="
10                         + Thread.currentThread().getName());
11             }
12         } catch (InterruptedException e) {
13             e.printStackTrace();
14         }
15     }
16 
17     public void synNotifyMethod(Object lock) {
18         try {
19             synchronized (lock) {
20                 System.out.println("begin notify() ThreadName="
21                         + Thread.currentThread().getName() + " time="
22                         + System.currentTimeMillis());
23                 lock.notify();
24                 Thread.sleep(5000);
25                 System.out.println("  end notify() ThreadName="
26                         + Thread.currentThread().getName() + " time="
27                         + System.currentTimeMillis());
28             }
29         } catch (InterruptedException e) {
30             e.printStackTrace();
31         }
32     }
33 }

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

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

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

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

接下來是兩個線程類,線程類ThreadA調用testMethod()方法執行lock.wait();時被掛起,另外一個線程類ThreadB調用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 ThreadB extends Thread {
	private Object lock;

	public ThreadB(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) {
		Object lock = new Object();

		ThreadA a = new ThreadA(lock);

		ThreadB b = new ThreadB(lock);

		a.start();

		b.start();
	}
}

輸出結果:
begin wait() ThreadName=Thread-0
begin notify() ThreadName=Thread-1 time=1554183779889
  end notify() ThreadName=Thread-1 time=1554183784889
  end wait() ThreadName=Thread-0

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

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

1)notify/notifyAll

2)wait()/wait(long)

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

1)interrupt()

2)sleep()/sleep(long)

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

使用線程的interrupt()方法,能夠終止處於阻塞狀態中的線程。api介紹以下:

調用線程的wait(), wait(long)或wait(long, int)會讓它進入等待(阻塞)狀態,或者調用線程的join(), join(long), join(long, int), sleep(long), sleep(long, int)也會讓它進入阻塞狀態。若線程在阻塞狀態時,調用了它的interrupt()方法,那麼它的「阻塞狀態」會被清除而且會收到一個InterruptedException異常。例如,線程經過wait()進入阻塞狀態,此時經過interrupt()中斷該線程;調用interrupt()會當即將線程的中斷標記設爲「true」,可是因爲線程處於阻塞狀態,因此該「中斷標記」會當即被清除爲「false」,同時,會產生一個InterruptedException的異常。

實例以下:

1 public class Test {
 2 
 3     public static void main(String[] args) {
 4 
 5         try {
 6             Object lock = new Object();
 7 
 8             ThreadA a = new ThreadA(lock);
 9             a.start();
10 
11             Thread.sleep(5000);
12 
13             a.interrupt();
14         } catch (InterruptedException e) {
15             e.printStackTrace();
16         }
17     }
18 }


輸出結果:
java.lang.InterruptedException: wait interrupted
	at java.lang.Thread.sleep(Native Method)
	at Thread3.run(Thread3.java:13)

④notify 通知的順序不能錯

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

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

之前一直不明白 當在線程的run()方法中須要測試某個條件時,爲何用while,而不用if?

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

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

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

相關文章
相關標籤/搜索