轉自:http://blog.chinaunix.net/uid-122937-id-215913.htmlcss
1. 線程的掛起和喚醒
掛起其實是讓線程進入「非可執行」狀態下,在這個狀態下CPU不會分給線程時間片,進入這個狀態能夠用來暫停一個線程的運行;在線程掛起後,能夠經過從新喚醒線程來使之恢復運行。
掛起的緣由多是以下幾種狀況:
(1)經過調用sleep()方法使線程進入休眠狀態,線程在指定時間內不會運行。
(2)經過調用join()方法使線程掛起,使本身等待另外一個線程的結果,直到另外一個線程執行完畢爲止。
(3)經過調用wait()方法使線程掛起,直到線程獲得了notify()和notifyAll()消息,線程纔會進入「可執行」狀態。
(4)使用suspend掛起線程後,能夠經過resume方法喚醒線程。
雖然suspend和resume能夠很方便地使線程掛起和喚醒,但因爲使用這兩個方法可能會形成死鎖,所以,這兩個方法被標識爲deprecated(抗議)標記,這代表在之後的jdk版本中這兩個方法可能被刪除,因此儘可能不要使用這兩個方法來操做線程。
調用sleep()、yield()、suspend()的時候並無被釋放鎖
調用wait()的時候釋放當前對象的鎖
wait()方法表示,放棄當前對資源的佔有權,一直等到有線程通知,纔會運行後面的代碼。
notify()方法表示,當前的線程已經放棄對資源的佔有,通知等待的線程來得到對資源的佔有權,可是隻有一個線程可以從wait狀態中恢復,而後繼續運行wait()後面的語句。
notifyAll()方法表示,當前的線程已經放棄對資源的佔有,通知全部的等待線程從wait()方法後的語句開始運行。
2.等待和鎖實現資源競爭
等待機制與鎖機制是密切關聯的,對於須要競爭的資源,首先用synchronized確保這段代碼只能一個線程執行,能夠再設置一個標誌位condition判斷該資源是否準備好,若是沒有,則該線程釋放鎖,本身進入等待狀態,直到接收到notify,程序從wait處繼續向下執行。
html
- synchronized(obj) {
- while(!condition) {
- obj.wait();
- }
- obj.doSomething();
- }
以上程序表示只有一個線程A得到了obj鎖後,發現條件condition不知足,沒法繼續下一處理,因而線程A釋放該鎖,進入wait()。
在另外一線程B中,若是B更改了某些條件,使得線程A的condition條件知足了,就能夠喚醒線程A:java
- synchronized(obj) {
- condition = true;
- obj.notify();
- }
須要注意的是:
# 調用obj的wait(), notify()方法前,必須得到obj鎖,也就是必須寫在synchronized(obj) {...} 代碼段內。
# 調用obj.wait()後,線程A就釋放了obj的鎖,不然線程B沒法得到obj鎖,也就沒法在synchronized(obj) {...} 代碼段內喚醒A。
# 當obj.wait()方法返回後,線程A須要再次得到obj鎖,才能繼續執行。
# 若是A1,A2,A3都在obj.wait(),則B調用obj.notify()只能喚醒A1,A2,A3中的一個(具體哪個由JVM決定)。
# obj.notifyAll()則能所有喚醒A1,A2,A3,可是要繼續執行obj.wait()的下一條語句,必須得到obj鎖,所以,A1,A2,A3只有一個有機會得到鎖繼續執行,例如A1,其他的須要等待A1釋放obj鎖以後才能繼續執行。
# 當B調用obj.notify/notifyAll的時候,B正持有obj鎖,所以,A1,A2,A3雖被喚醒,可是仍沒法得到obj鎖。直到B退出synchronized塊,釋放obj鎖後,A1,A2,A3中的一個纔有機會得到鎖繼續執行。
例1:單個線程對多個線程的喚醒
假設只有一個Game對象,但有3我的要玩,因爲只有一個遊戲資源,必須必然依次玩。web
- /**
- * 玩遊戲的人.
- * @version V1.0 ,2011-4-8
- * @author xiahui
- */
- public class Player implements Runnable {
- private final int id;
- private Game game;
- public Player(int id, Game game) {
- this.id = id;
- this.game = game;
- }
- public String toString() {
- return "Athlete<" + id + ">";
- }
- public int hashCode() {
- return new Integer(id).hashCode();
- }
-
- public void playGame() throws InterruptedException{
- System.out.println(this.toString() + " ready!");
- game.play(this);
- }
- public void run() {
- try {
- playGame();
- } catch (InterruptedException e) {
- System.out.println(this + " quit the game");
- }
- }
- }
遊戲類,只實例化一個設計模式
- import java.util.HashSet;
- import java.util.Iterator;
- import java.util.Set;
- /**
- * 遊戲類.
- * @version V1.0 ,2011-4-8
- * @author xiahui
- */
- public class Game implements Runnable {
- private boolean start = false;
- public void play(Player player) throws InterruptedException {
- synchronized (this) {
- while (!start)
- wait();
- if (start)
- System.out.println(player + " have played!");
- }
- }
- //通知全部玩家
- public synchronized void beginStart() {
- start = true;
- notifyAll();
- }
- public void run() {
- start = false;
- System.out.println("Ready......");
- System.out.println("Ready......");
- System.out.println("game start");
- beginStart();//通知全部玩家遊戲準備好了
- }
- public static void main(String[] args) {
- Set<Player> players = new HashSet<Player>();
- //實例化一個遊戲
- Game game = new Game();
-
- //實例化3個玩家
- for (int i = 0; i < 3; i++)
- players.add(new Player(i, game));
-
- //啓動3個玩家
- Iterator<Player> iter = players.iterator();
- while (iter.hasNext())
- new Thread(iter.next()).start();
- Thread.sleep(100);
-
- //遊戲啓動
- new Thread(game).start();
- }
- }
程序先啓動玩家,三個玩家競爭玩遊戲,但只能有一個進入play,其餘二個等待,進入的玩家發現遊戲未準備好,因此wait,等遊戲準備好後,依次玩。
運行結果多線程
- Athlete<0> ready!
- Athlete<1> ready!
- Athlete<2> ready!
- Ready......
- Ready......
- game start
- Athlete<2> have played!
- Athlete<1> have played!
- Athlete<0> have played!
3.一次喚醒一個線程
一次喚醒全部玩家,但只有一個玩家能玩,不如一個一個喚醒
將上面的代碼修改以下ui
- public void play(Player player) throws InterruptedException {
- synchronized (this) {
- while (!start)
- wait();
- if (start){
- System.out.println(player + " have played!");
- notify();//玩完後,通知下一個玩家來玩
- }
- }
- }
- //通知一個玩家
- public synchronized void beginStart() {
- start = true;
- notify();
- }
4.suspend掛起
該方法已不建議使用,例子以下
例2:suspend方法進行掛起和喚醒this
- /**
- * suspend方法進行掛起和喚醒.
- * @version V1.0 ,2011-3-27
- * @author xiahui
- */
- public class SuspendThread implements Runnable{
- public void run() {
- try {
- Thread.sleep(10);
- } catch (Exception e) {
- System.out.println(e);
- }
- for (int i = 0; i <= 1; i ) {
- System.out.println(Thread.currentThread().getName() ":" i);
- }
- }
- public static void main(String args[]) throws Exception {
- Thread th1 = new Thread(new SuspendThread(),"thread1");
- Thread th2 = new Thread(new SuspendThread(),"thread2");
- System.out.println("Starting " th1.getName() "...");
- th1.start();
- System.out.println("Suspending " th1.getName() "...");
- //Suspend the thread.
- th1.suspend();
- th2.start();
- th2.join();
- // Resume the thread.
- th1.resume();
- }
- }
運行結果spa
- Starting thread1...
- Suspending thread1...
- thread2:0
- thread2:1
- thread1:0
- thread1:1
注意:
若是註釋掉//th2.join();則thread2運行後,主線程會直接執行thread1的resume,運行結果可能會是.net
- Starting thread1...
- Suspending thread1...
- thread1:0
- thread1:1
- thread2:0
- thread2:1
參考文獻
1.Java多線程設計模式:瞭解wait/notify機制. http://webservices.ctocio.com.cn/wsjavtec/335/8580335.shtml2.Java中使用wait()與notify()實現線程間協做. http://www.bianceng.cn/Programming/Java/201103/25215.htm