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,如需转载请自行联系原作者
相关文章
|
6天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
37 6
|
19天前
|
存储 监控 小程序
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
|
15天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
15天前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
40 3
|
16天前
|
监控 Java 开发者
深入理解Java中的线程池实现原理及其性能优化####
本文旨在揭示Java中线程池的核心工作机制,通过剖析其背后的设计思想与实现细节,为读者提供一份详尽的线程池性能优化指南。不同于传统的技术教程,本文将采用一种互动式探索的方式,带领大家从理论到实践,逐步揭开线程池高效管理线程资源的奥秘。无论你是Java并发编程的初学者,还是寻求性能调优技巧的资深开发者,都能在本文中找到有价值的内容。 ####
|
19天前
|
监控 Java 数据库连接
Java线程管理:守护线程与用户线程的区分与应用
在Java多线程编程中,线程可以分为守护线程(Daemon Thread)和用户线程(User Thread)。这两种线程在行为和用途上有着明显的区别,了解它们的差异对于编写高效、稳定的并发程序至关重要。
27 2
|
19天前
|
监控 Java 开发者
Java线程管理:守护线程与本地线程的深入剖析
在Java编程语言中,线程是程序执行的最小单元,它们可以并行执行以提高程序的效率和响应性。Java提供了两种特殊的线程类型:守护线程和本地线程。本文将深入探讨这两种线程的区别,并探讨它们在实际开发中的应用。
27 1
|
7月前
|
存储 安全 Java
深入理解Java并发编程:线程安全与锁机制
【5月更文挑战第31天】在Java并发编程中,线程安全和锁机制是两个核心概念。本文将深入探讨这两个概念,包括它们的定义、实现方式以及在实际开发中的应用。通过对线程安全和锁机制的深入理解,可以帮助我们更好地解决并发编程中的问题,提高程序的性能和稳定性。
|
4月前
|
存储 安全 Java
解锁Java并发编程奥秘:深入剖析Synchronized关键字的同步机制与实现原理,让多线程安全如磐石般稳固!
【8月更文挑战第4天】Java并发编程中,Synchronized关键字是确保多线程环境下数据一致性与线程安全的基础机制。它可通过修饰实例方法、静态方法或代码块来控制对共享资源的独占访问。Synchronized基于Java对象头中的监视器锁实现,通过MonitorEnter/MonitorExit指令管理锁的获取与释放。示例展示了如何使用Synchronized修饰方法以实现线程间的同步,避免数据竞争。掌握其原理对编写高效安全的多线程程序极为关键。
71 1
|
5月前
|
安全 Java 开发者
Java并发编程中的线程安全问题及解决方案探讨
在Java编程中,特别是在并发编程领域,线程安全问题是开发过程中常见且关键的挑战。本文将深入探讨Java中的线程安全性,分析常见的线程安全问题,并介绍相应的解决方案,帮助开发者更好地理解和应对并发环境下的挑战。【7月更文挑战第3天】
107 0
下一篇
DataWorks