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.


本文转自zhangjunhd51CTO博客,原文链接:http://blog.51cto.com/zhangjunhd/71387,如需转载请自行联系原作者
目录
打赏
0
0
0
0
69
分享
相关文章
|
10天前
|
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
124 60
【Java并发】【线程池】带你从0-1入门线程池
|
6天前
|
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
当我们创建一个`ThreadPoolExecutor`的时候,你是否会好奇🤔,它到底发生了什么?比如:我传的拒绝策略、线程工厂是啥时候被使用的? 核心线程数是个啥?最大线程数和它又有什么关系?线程池,它是怎么调度,我们传入的线程?...不要着急,小手手点上关注、点赞、收藏。主播马上从源码的角度带你们探索神秘线程池的世界...
52 0
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
88 14
Java 面试必问!线程构造方法和静态块的执行线程到底是谁?
大家好,我是小米。今天聊聊Java多线程面试题:线程类的构造方法和静态块是由哪个线程调用的?构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节有助于掌握Java多线程机制。下期再见! 简介: 本文通过一个常见的Java多线程面试题,详细讲解了线程类的构造方法和静态块是由哪个线程调用的。构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节对掌握Java多线程编程至关重要。
54 13
【JAVA】封装多线程原理
Java 中的多线程封装旨在简化使用、提高安全性和增强可维护性。通过抽象和隐藏底层细节,提供简洁接口。常见封装方式包括基于 Runnable 和 Callable 接口的任务封装,以及线程池的封装。Runnable 适用于无返回值任务,Callable 支持有返回值任务。线程池(如 ExecutorService)则用于管理和复用线程,减少性能开销。示例代码展示了如何实现这些封装,使多线程编程更加高效和安全。
|
2月前
|
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
121 17
Java 多线程 面试题
Java 多线程 相关基础面试题
时间轮-Java实现篇
在前面的文章《[时间轮-理论篇](https://developer.aliyun.com/article/910513)》讲了时间轮的一些理论知识,然后根据理论知识。我们自己来实现一个简单的时间轮。
|
3月前
|
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等