Java:使用wait()与notify()实现线程间协作

本文探讨了并发编程中的关键概念,包括使用wait()与notify()实现线程间的协作,通过实例展示了单个线程如何唤醒多个线程,以及忙等待与wait()方法的应用。此外,介绍了使用wait()与notify()改写忙等待机制的技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用 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.

转载于:https://my.oschina.net/u/559701/blog/67733

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值