zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    vue 文件分段上传
    深度clone
    js 导出excel
    js 校验
    设计模式原则
    多态
    数据库sql
    Redis快速入门
    C#中使用Redis学习二 在.NET4.5中使用redis hash操作
    在c#中使用servicestackredis操作redis
  • 原文地址:https://www.cnblogs.com/kelin1314/p/4020643.html
Copyright © 2011-2022 走看看