Java:使用wait()與notify()實現線程間協做

使用 wait() notify()/notifyAll() 能夠使得多個任務之間彼此協做。
1. wait() notify()/notifyAll()
調用 sleep() yield() 的時候鎖並無被釋放,而調用 wait() 將釋放鎖。這樣另外一個任務(線程)能夠得到當前對象的鎖,從而進入它的 synchronized 方法中。能夠經過 notify()/notifyAll() ,或者時間到期,從 wait() 中恢復執行。
只能在同步控制方法或同步塊中調用 wait() notify() notifyAll() 。若是在非同步的方法裏調用這些方法,在運行時會拋出 IllegalMonitorStateException 異常。
2. 模擬單個線程對多個線程的喚醒
模擬線程之間的協做。Game 類有2個同步方法 prepare() go() 。標誌位 start 用於判斷當前線程是否須要 wait() 。Game 類的實例首先啓動全部的 Athele 類實例,使其進入 wait() 狀態,在一段時間後,改變標誌位並 notifyAll() 全部處於 wait 狀態的 Athele 線程。
Game.java
package concurrency;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

class Athlete implements Runnable {
    private final int id;
    private Game game;

    public Athlete(int id, Game game) {
      this.id = id;
      this.game = game;
    }

    public boolean equals(Object o) {
      if (!(o instanceof Athlete))
        return false;
      Athlete athlete = (Athlete) o;
      return id == athlete.id;
    }

    public String toString() {
      return "Athlete<" + id + ">";
    }

    public int hashCode() {
      return new Integer(id).hashCode();
    }

    public void run() {
      try {
        game.prepare(this);
      } catch (InterruptedException e) {
        System.out.println(this + " quit the game");
      }
    }
  }

public class Game implements Runnable {
    private Set<Athlete> players = new HashSet<Athlete>();
    private boolean start = false;

    public void addPlayer(Athlete one) {
      players.add(one);
    }

    public void removePlayer(Athlete one) {
      players.remove(one);
    }

    public Collection<Athlete> getPlayers() {
      return Collections.unmodifiableSet(players);
    }

    public void prepare(Athlete athlete) throws InterruptedException {
      System.out.println(athlete + " ready!");
      synchronized (this) {
        while (!start)
        wait();
        if (start)
          System.out.println(athlete + " go!");
      }
    }

    public synchronized void go() {
      notifyAll();
    }
    
    public void ready() {
      Iterator<Athlete> iter = getPlayers().iterator();
      while (iter.hasNext())
        new Thread(iter.next()).start();
    }

    public void run() {
      start = false;
      System.out.println("Ready......");
      System.out.println("Ready......");
      System.out.println("Ready......");
      ready();
      start = true;
      System.out.println("Go!");
      go();
    }

    public static void main(String[] args) {
      Game game = new Game();
      for (int i = 0; i < 10; i++)
        game.addPlayer(new Athlete(i, game));
      new Thread(game).start();
    }
}
結果:
Ready......
Ready......
Ready......
Athlete<0> ready!
Athlete<1> ready!
Athlete<2> ready!
Athlete<3> ready!
Athlete<4> ready!
Athlete<5> ready!
Athlete<6> ready!
Athlete<7> ready!
Athlete<8> ready!
Athlete<9> ready!
Go!
Athlete<9> go!
Athlete<8> go!
Athlete<7> go!
Athlete<6> go!
Athlete<5> go!
Athlete<4> go!
Athlete<3> go!
Athlete<2> go!
Athlete<1> go!
Athlete<0> go!
3. 模擬忙等待過程
MyObject 類的實例是被觀察者,當觀察事件發生時,它會通知一個 Monitor 類的實例(通知的方式是改變一個標誌位)。而此 Monitor 類的實例是經過忙等待來不斷的檢查標誌位是否變化。
BusyWaiting.java
import java.util.concurrent.TimeUnit;

class MyObject implements Runnable {
    private Monitor monitor;

    public MyObject(Monitor monitor) {
      this.monitor = monitor;
    }

    public void run() {
      try {
        TimeUnit.SECONDS.sleep(3);
        System.out.println("i'm going.");
        monitor.gotMessage();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
}

class Monitor implements Runnable {
    private volatile boolean go = false;

    public void gotMessage() throws InterruptedException {
      go = true;
    }

    public void watching() {
      while (go == false)
        ;
      System.out.println("He has gone.");
    }

    public void run() {
      watching();
    }
}

public class BusyWaiting {
    public static void main(String[] args) {
      Monitor monitor = new Monitor();
      MyObject o = new MyObject(monitor);
      new Thread(o).start();
      new Thread(monitor).start();
    }
}

結果:
i'm going.
He has gone.
4. 使用 wait() notify() 改寫上面的例子
下面的例子經過 wait() 來取代忙等待機制,當收到通知消息時, notify 當前 Monitor 類線程。
Wait.java
package concurrency.wait;

import java.util.concurrent.TimeUnit;

class MyObject implements Runnable {
    private Monitor monitor;

    public MyObject(Monitor monitor) {
      this.monitor = monitor;
    }

    public void run() {
      try {
        TimeUnit.SECONDS.sleep(3);
        System.out.println("i'm going.");
        monitor.gotMessage();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
}

class Monitor implements Runnable {
    private volatile boolean go = false;

    public synchronized void gotMessage() throws InterruptedException {
      go = true;
      notify();
    }

    public synchronized void watching() throws InterruptedException {
      while (go == false)
        wait();
      System.out.println("He has gone.");
    }

    public void run() {
      try {
        watching();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
}

public class Wait {
    public static void main(String[] args) {
      Monitor monitor = new Monitor();
      MyObject o = new MyObject(monitor);
      new Thread(o).start();
      new Thread(monitor).start();
    }
}

結果:
i'm going.
He has gone.
相關文章
相關標籤/搜索