转自: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. public void playGame() throws InterruptedException{
  20. System.out.println(this.toString() + " ready!");
  21. game.play(this);
  22. }
  23. public void run() {
  24. try {
  25. playGame();
  26. } catch (InterruptedException e) {
  27. System.out.println(this + " quit the game");
  28. }
  29. }
  30. }

游戏类,只实例化一个

  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. //实例化3个玩家
  36. for (int i = 0; i < 3; i++)
  37. players.add(new Player(i, game));
  38. //启动3个玩家
  39. Iterator<Player> iter = players.iterator();
  40. while (iter.hasNext())
  41. new Thread(iter.next()).start();
  42. Thread.sleep(100);
  43. //游戏启动
  44. new Thread(game).start();
  45. }
  46. }

程序先启动玩家,三个玩家竞争玩游戏,但只能有一个进入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线程技术6_线程的挂起和唤醒[转]的更多相关文章

  1. Java后台技术(线程安全)

    前端时间一个同事因为后台线程安全问题出了一次生产事故,今天我就对线程安全问题进行一次总结. 首先,我们来大致看以下我同事写的代码,代码我进行了精简,大致如下: for (final String re ...

  2. 【java并发】传统线程技术中创建线程的两种方式

    传统的线程技术中有两种创建线程的方式:一是继承Thread类,并重写run()方法:二是实现Runnable接口,覆盖接口中的run()方法,并把Runnable接口的实现扔给Thread.这两种方式 ...

  3. Java并发编程与技术内幕:线程池深入理解

    摘要: 本文主要讲了Java当中的线程池的使用方法.注意事项及其实现源码实现原理,并辅以实例加以说明,对加深Java线程池的理解有很大的帮助. 首先,讲讲什么是线程池?照笔者的简单理解,其实就是一组线 ...

  4. Java面向对象 线程技术 -- 下篇

     Java面向对象 线程技术 -- 下篇 知识概要:              (1)线程间的通信           生产者 - 消费者 (2)生产者消费者案例优化 (3)守护线程 (4)停止线 ...

  5. Java多线程系列——过期的suspend()挂起、resume()继续执行线程

    简述 这两个操作就好比播放器的暂停和恢复. 但这两个 API 是过期的,也就是不建议使用的. 不推荐使用 suspend() 去挂起线程的原因,是因为 suspend() 在导致线程暂停的同时,并不会 ...

  6. JAVA多线程提高一:传统线程技术&传统定时器Timer

    前面我们已经对多线程的基础知识有了一定的了解,那么接下来我们将要对多线程进一步深入的学习:但在学习之前我们还是要对传统的技术进行一次回顾,本章我们回顾的则是:传统线程技术和传统的定时器实现. 一.传统 ...

  7. Java并发基础01. 传统线程技术中创建线程的两种方式

    传统的线程技术中有两种创建线程的方式:一是继承Thread类,并重写run()方法:二是实现Runnable接口,覆盖接口中的run()方法,并把Runnable接口的实现扔给Thread.这两种方式 ...

  8. Java面向对象 线程技术--上篇

     Java面向对象   线程 知识概要:                   (1)线程与进程 (2)自定义线程的语法结构 (3)多线程概念理解 (4)多线程状态图 (5)多线程--卖票 (6)同 ...

  9. 转:Java并发编程与技术内幕:线程池深入理解

    版权声明:本文为博主林炳文Evankaka原创文章,转载请注明出处http://blog.csdn.net/evankaka 目录(?)[+] ); } catch (InterruptedExcep ...

随机推荐

  1. Oracle常用plsql

    String aggr http://www.oracle-base.com/articles/misc/string-aggregation-techniques.php     SELECT 'S ...

  2. appium 常用api介绍(2)

    前言:接着上一篇继续讲常用的一些api 参考博文:http://blog.csdn.net/bear_w/article/details/50330565 1.send_keys send_keys( ...

  3. 酷派5890 ROM教程

    一.前言 ROM出处:http://www.in189.com/thread-975035-1-1.html 名称:酷派5890官方056精简优化包V2.2,G卡可用+已ROOT+可用大运存 更新日期 ...

  4. QTP基础学习(二)启动与设置

    1.启动QTP选择要求的Add-in 默认带有3个Add-in,之后可以安装其他的Add-in,如.net的Add-in 2.设置QTP的选项 点击Tools-Options,弹出如下框: 3.建立记 ...

  5. Linux下检测IP地址冲突及解决方法

    问题说明:在公司办公网内的一台物理机A上安装了linux系统(ip:192.168.9.120),在上面部署了jenkins,redmine,svn程序.由于是在办公网内,这台机器和同事电脑都是在同一 ...

  6. Linux下利用CGroup控制CPU、内存以及IO的操作记录

    CGroup及其子系统的介绍在这里就不赘述了,可以参考:Linux下CGroup使用说明梳理废话不多说,这里记录下利用CGroup控制CPU.内存以及IO的操作记录: libcgroup工具安装这里以 ...

  7. [5]Telerik Extensions for ASP.NET MVC 开发问题

    1.Controller获取不到checkedNodes的问题 HTML @(Html.Telerik().TreeView()        .Name("TreeView")  ...

  8. 关于RACK的一点简单介绍

    0 前言 如有错误欢迎指出,如需转载,请注明原文链接. 1 Rack是什么 一句话介绍,Rack是一个Web接口,定义了一系列的标准,具体实现的工作是交给服务器(puma, thin等)做的. 如果你 ...

  9. [Elixir007] on_definition规范函数定义时的各种潜规则

    1.需求 写一个基于memcache的cache模块, 需要在key前面加上特定的前缀, 所以user cache的原始的store函数应该写成 # user.exdef store(user_id, ...

  10. [转]php返回json数据中文显示的问题

    转自 : http://blog.csdn.net/superbirds/article/details/8091910 解决方法:   <?php    function Notice(){  ...