`

java线程技术6_线程的挂起和唤醒 (转)

 
阅读更多

转载自:http://blog.chinaunix.net/uid-122937-id-215913.html

 

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处继续向下执行。

  1. synchronized(obj) {
  2.   while(!condition) {
  3.    obj.wait();
  4.   }
  5.   obj.doSomething();
  6. }

以上程序表示只有一个线程A获得了obj锁后,发现条件condition不满足,无法继续下一处理,于是线程A释放该锁,进入wait()。

      在另一线程B中,如果B更改了某些条件,使得线程A的condition条件满足了,就可以唤醒线程A:

  1. synchronized(obj) {
  2.  condition = true;
  3.  obj.notify();
  4. }

需要注意的是:
  # 调用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个人要玩,由于只有一个游戏资源,必须必然依次玩。

  1. /**
  2.  * 玩游戏的人.
  3.  * @version V1.0 ,2011-4-8
  4.  * @author xiahui
  5.  */
  6. public class Player implements Runnable {
  7.     private final int id;
  8.     private Game game;
  9.     public Player(int id, Game game) {
  10.         this.id = id;
  11.         this.game = game;
  12.     }
  13.     public String toString() {
  14.         return "Athlete<" + id + ">";
  15.     }
  16.     public int hashCode() {
  17.         return new Integer(id).hashCode();
  18.     }
  19.     
  20.     public void playGame() throws InterruptedException{
  21.         System.out.println(this.toString() + " ready!");
  22.         game.play(this);
  23.     }
  24.     public void run() {
  25.         try {
  26.             playGame();
  27.         } catch (InterruptedException e) {
  28.             System.out.println(this + " quit the game");
  29.         }
  30.     }
  31. }

游戏类,只实例化一个

  1. import java.util.HashSet;
  2. import java.util.Iterator;
  3. import java.util.Set;
  4. /**
  5.  * 游戏类.
  6.  * @version V1.0 ,2011-4-8
  7.  * @author xiahui
  8.  */
  9. public class Game implements Runnable {
  10.     private boolean start = false;
  11.     public void play(Player player) throws InterruptedException {
  12.         synchronized (this) {
  13.             while (!start)
  14.                 wait();
  15.             if (start)
  16.                 System.out.println(player + " have played!");
  17.         }
  18.     }
  19.     //通知所有玩家
  20.     public synchronized void beginStart() {
  21.         start = true;
  22.         notifyAll();
  23.     }
  24.     public void run() {
  25.         start = false;
  26.         System.out.println("Ready......");
  27.         System.out.println("Ready......");
  28.         System.out.println("game start");
  29.         beginStart();//通知所有玩家游戏准备好了
  30.     }
  31.     public static void main(String[] args) {
  32.         Set<Player> players = new HashSet<Player>();
  33.         //实例化一个游戏
  34.         Game game = new Game();
  35.         
  36.         //实例化3个玩家
  37.         for (int i = 0; i < 3; i++)
  38.             players.add(new Player(i, game));
  39.         
  40.         //启动3个玩家
  41.         Iterator<Player> iter = players.iterator();
  42.         while (iter.hasNext())
  43.             new Thread(iter.next()).start();
  44.         Thread.sleep(100);
  45.         
  46.         //游戏启动
  47.         new Thread(game).start();
  48.     }
  49. }

程序先启动玩家,三个玩家竞争玩游戏,但只能有一个进入play,其他二个等待,进入的玩家发现游戏未准备好,所以wait,等游戏准备好后,依次玩。
运行结果

  1. Athlete<0> ready!
  2. Athlete<1> ready!
  3. Athlete<2> ready!
  4. Ready......
  5. Ready......
  6. game start
  7. Athlete<2> have played!
  8. Athlete<1> have played!
  9. Athlete<0> have played!


3.一次唤醒一个线程
      一次唤醒所有玩家,但只有一个玩家能玩,不如一个一个唤醒
将上面的代码修改如下

  1.     public void play(Player player) throws InterruptedException {
  2.         synchronized (this) {
  3.             while (!start)
  4.                 wait();
  5.             if (start){
  6.                 System.out.println(player + " have played!");
  7.                 notify();//玩完后,通知下一个玩家来玩
  8.             }
  9.         }
  10.     }
  11.     //通知一个玩家
  12.     public synchronized void beginStart() {
  13.         start = true;
  14.         notify();
  15.     }


4.suspend挂起
      该方法已不建议使用,例子如下
例2:suspend方法进行挂起和唤醒

  1. /**
  2.  * suspend方法进行挂起和唤醒.
  3.  * @version V1.0 ,2011-3-27 
  4.  * @author xiahui
  5.  */
  6. public class SuspendThread implements Runnable{
  7.     public void run() {
  8.         try {
  9.             Thread.sleep(10);
  10.         } catch (Exception e) {
  11.             System.out.println(e);
  12.         }
  13.         for (int i = 0; i <= 1; i ) {
  14.             System.out.println(Thread.currentThread().getName() ":" i);
  15.         }
  16.     }
  17.     public static void main(String args[]) throws Exception {
  18.         Thread th1 = new Thread(new SuspendThread(),"thread1");
  19.         Thread th2 = new Thread(new SuspendThread(),"thread2");
  20.         System.out.println("Starting " th1.getName() "...");
  21.         th1.start();
  22.         System.out.println("Suspending " th1.getName() "...");
  23.         //Suspend the thread.
  24.         th1.suspend();
  25.         th2.start();
  26.         th2.join();
  27.         // Resume the thread.
  28.         th1.resume();
  29.     }
  30. }

运行结果

  1. Starting thread1...
  2. Suspending thread1...
  3. thread2:0
  4. thread2:1
  5. thread1:0
  6. thread1:1

注意:
      如果注释掉//th2.join();则thread2运行后,主线程会直接执行thread1的resume,运行结果可能会是

  1. Starting thread1...
  2. Suspending thread1...
  3. thread1:0
  4. thread1:1
  5. thread2:0
  6. thread2:1



参考文献
1.Java多线程设计模式:了解wait/notify机制. http://webservices.ctocio.com.cn/wsjavtec/335/8580335.shtml
2.Java中使用wait()与notify()实现线程间协作. http://www.bianceng.cn/Programming/Java/201103/25215.htm

 

 

 

另转:

 为避免轮询,java包含了通过wait(),notify()和notifyAll()方法实现的一个进程间通信机制。这些方法在对象中是用final方法实现的,所以所有的类都含有它们。这三个方法仅在synchronized方法中才能被调用。尽管这些方法从计算机科学远景方向上来说具有概念的高度先进性,实际很简单:
 【】wait()告知被调用的线程放弃管程进入睡眠直到其他线程进入相同管程并且调用notify()。
 【】notify()恢复相同对象中第一个调用wait()的线程。
 【】notifyAll()恢复相同对象中所有调用wait()的线程。具有高优先级的线程最先运行。

分享到:
评论

相关推荐

    线程的挂起、唤醒和终止

    在编程中,我们常常需要对线程进行控制,包括挂起、唤醒和终止等操作,以实现更加灵活的程序设计。下面我们将深入探讨这些知识点。 首先,线程的挂起是指将一个正在运行的线程暂停执行,使其进入等待状态。这通常...

    JAVA100例之实例66 实现对线程的控制,中断、挂起、恢复、停止

    2. **线程挂起**: 在Java中,`Thread.suspend()`和`Thread.resume()`方法已经被废弃,因为它们可能导致死锁。现在,推荐使用`wait()`和`notify()`或`notifyAll()`来实现线程的挂起和恢复。当一个线程调用`wait()`...

    java多线程ppt

    java多线程PPT 多线程基本概念 创建线程的方式 线程的挂起与唤醒 多线程问题

    Java多线程机制(讲述java里面与多线程有关的函数)

    挂起线程通常通过调用`sleep()`或`wait()`方法实现,恢复线程可能通过`notify()`或`notifyAll()`唤醒,终止线程则可以通过`interrupt()`方法发送中断请求,但线程本身需要检查`isInterrupted()`或`...

    线程的挂起与恢复,以及如何创建线程

    线程的挂起和恢复通常是为了控制线程的执行顺序或优化资源使用。然而,现代编程语言并不推荐直接使用这些操作,因为它们可能导致死锁或不确定的行为。在Java中,`suspend()`和`resume()`方法已弃用,因为它们可能会...

    Java 多线程介绍

    总之,虽然Java多线程提供了强大的并行处理能力,但在实际应用中应避免使用已废弃的线程控制方法,如`suspend()`和`resume()`,转而采用更安全、更高效的并发控制技术。这样不仅能够提高程序的性能和稳定性,还能...

    java线程同步及通信

    3. **线程挂起、恢复与终止**: 在Java中,可以通过调用线程的`sleep()`方法使其挂起一段时间,然后自动恢复。`Callme.java`的`call()`方法中就使用了`sleep()`。线程的终止通常由线程自己决定(通过`System.exit()...

    同步的发送线程与接收线程.rar_线程

    条件变量用于在线程等待某个条件满足时挂起它们,条件满足时再唤醒。在C++中,可以使用`std::condition_variable`。 读写锁允许多个读取线程同时访问资源,但只允许一个写入线程。这样可以提高并发性能,因为通常读...

    java线程学习笔记

    引起线程阻塞挂起的原因: 1. Thread.sleep() 方法:线程睡眠一段时间,不会释放锁资源。 2. Suspend() 方法:暂停线程,不会释放锁资源,容易造成死锁,建议不使用,resume() 方法恢复线程。 3. Wait() 方法:使...

    java中线程挂起的几种方式详解

    Java中线程挂起的几种方式详解是Java编程中一个常见的技术,主要用于控制线程的执行和暂停。在Java中提供了三种方式来实现线程挂起:suspend/resume、wait/notify、notifyAll和park/unpark。 1. suspend/resume方式...

    Java消费者生产者|线程等待线程唤醒WaitNotifyDemo

    Java消费者生产者模式是一种经典的多线程编程模型,它通过线程同步机制来协调生产者与消费者之间的数据处理,确保资源的有效利用和程序的稳定运行。在这个模型中,生产者负责生成数据,而消费者则负责消费这些数据。...

    Java线程Dump分析工具jstack解析及使用场景

    Java线程Dump分析工具jstack是Java开发人员和运维人员常用的诊断工具,它能够帮助我们了解Java应用程序中线程的状态,以及线程的执行轨迹。本文将深入解析jstack的使用方法及其在不同场景下的应用。 jstack命令的...

    JAVA多线程操作方法实用大全

    在Java编程中,多线程是并发执行任务的关键技术,它可以提高程序的效率和响应性。本文将深入探讨Java中的多线程操作方法,包括线程控制的基本方法、中断和睡眠以及相关示例。 首先,了解线程的基本状态至关重要。...

    java多媒体与多线程处理实验

    9. **线程挂起与恢复**:`wait()`和`notify()`/`notifyAll()`方法用于线程间的同步通信,前者使线程进入等待状态,后者唤醒等待的线程。 10. **动画原理**:动画效果通常通过快速连续显示一系列图像实现,每帧图像...

    Java 实例 - 线程挂起源代码+详细指导教程.zip

    通过阅读和运行这些代码,你可以更好地理解如何在实际项目中应用线程挂起技术。 此外,学习线程挂起不仅限于基本方法的使用,还应关注如何避免死锁、饥饿和优先级反转等问题。理解线程的生命周期、状态转换(新建、...

    Java学习教程-探究JAVA线程状态及转化视频

    如`join()`用于等待线程完成,`interrupt()`用于中断线程,`yield()`让当前线程暂停并让出CPU时间,以及`suspend()`和`resume()`(已废弃)用于临时挂起和恢复线程。 在学习Java线程时,还应掌握同步机制,包括`...

    _Java多线程编程详解.doc

    2. 挂起和唤醒线程:可以通过wait()、notify()和notifyAll()方法控制线程的挂起与唤醒,实现线程间同步和协作。 3. 终止线程的三种方法:正常结束(run()方法执行完毕)、异常结束(run()方法抛出未捕获的异常)、...

    java面试题之多线程

    - 线程有四种基本状态:运行(Runnable)、就绪(Blocked)、挂起(Waiting/Timed_Waiting)和结束(Terminated)。线程状态的变化受到调度器的影响。 8. **synchronized 与 Lock**: - **相同点**:两者都能实现...

    Java多线程完全概念题

    - suspend()已废弃,它会使线程挂起,但不会释放任何锁,容易导致死锁,应避免使用。 - wait()使当前线程等待,释放对象锁,直到其他线程调用同一对象的notify()或notifyAll()唤醒。wait()必须在同步上下文中调用...

Global site tag (gtag.js) - Google Analytics