熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> JSP教程 >> 正文

為何會堵塞

2013-11-15 11:48:05  來源: JSP教程 

  
  堵塞狀態是前述四種狀態中最有趣的值得我們作進一步的探討線程被堵塞可能是由下述五方面的原因造成的
  () 調用sleep(毫秒數)使線程進入睡眠狀態在規定的時間內這個線程是不會運行的
  () 用suspend()暫停了線程的執行除非線程收到resume()消息否則不會返回可運行狀態
  () 用wait()暫停了線程的執行除非線程收到nofify()或者notifyAll()消息否則不會變成可運行(是的這看起來同原因非常相象但有一個明顯的區別是我們馬上要揭示的)
  () 線程正在等候一些IO(輸入輸出)操作完成
  () 線程試圖調用另一個對象的同步方法但那個對象處於鎖定狀態暫時無法使用
  亦可調用yield()(Thread類的一個方法)自動放棄CPU以便其他線程能夠運行然而假如調度機制覺得我們的線程已擁有足夠的時間並跳轉到另一個線程就會發生同樣的事情也就是說沒有什麼能防止調度機制重新啟動我們的線程線程被堵塞後便有一些原因造成它不能繼續運行
  下面這個例子展示了進入堵塞狀態的全部五種途徑它們全都存在於名為Blockingjava的一個文件中但在這兒采用散落的片斷進行解釋(大家可注意到片斷前後的Continued以及Continuing標志利用第章介紹的工具可將這些片斷連結到一起)首先讓我們看看基本的框架
  //: Blockingjava
  // Demonstrates the various ways a thread
  // can be blocked
  import javaawt*;
  import javaawtevent*;
  import javaapplet*;
  import javaio*;
  //////////// The basic framework ///////////
  class Blockable extends Thread {
   private Peeker peeker;
   protected TextField state = new TextField();
   protected int i;
   public Blockable(Container c) {
   cadd(state);
   peeker = new Peeker(this c);
   }
   public synchronized int read() { return i; }
   protected synchronized void update() {
   statesetText(getClass()getName()
   + state: i = + i);
   }
   public void stopPeeker() {
   // peekerstop(); Deprecated in Java
   peekerterminate(); // The preferred approach
   }
  }
  class Peeker extends Thread {
   private Blockable b;
   private int session;
   private TextField status = new TextField();
   private boolean stop = false;
   public Peeker(Blockable b Container c) {
   cadd(status);
   thisb = b;
   start();
   }
   public void terminate() { stop = true; }
   public void run() {
   while (!stop) {
   statussetText(bgetClass()getName()
   + Peeker + (++session)
   + ; value = + bread());
   try {
   sleep();
   } catch (InterruptedException e){}
   }
   }
  } ///:Continued
  Blockable類打算成為本例所有類的一個基礎類一個Blockable對象包含了一個名為state的TextField(文本字段)用於顯示出對象有關的信息用於顯示這些信息的方法叫作update()我們發現它用getClassgetName()來產生類名而不是僅僅把它打印出來這是由於update(不知道自己為其調用的那個類的准確名字因為那個類是從Blockable衍生出來的
  在Blockable中變動指示符是一個int i衍生類的run()方法會為其增值
  針對每個Bloackable對象都會啟動Peeker類的一個線程Peeker的任務是調用read()方法檢查與自己關聯的Blockable對象看看i是否發生了變化最後用它的status文本字段報告檢查結果注意read()和update()都是同步的要求對象的鎖定能自由解除這一點非常重要
   睡眠
  這個程序的第一項測試是用sleep()作出的
  ///:Continuing
  ///////////// Blocking via sleep() ///////////
  class Sleeper extends Blockable {
   public Sleeper(Container c) { super(c); }
   public synchronized void run() {
   while(true) {
   i++;
   update();
   try {
   sleep();
   } catch (InterruptedException e){}
   }
   }
  }
  class Sleeper extends Blockable {
   public Sleeper(Container c) { super(c); }
   public void run() {
   while(true) {
   change();
   try {
   sleep();
   } catch (InterruptedException e){}
   }
   }
   public synchronized void change() {
   i++;
   update();
   }
  } ///:Continued
  在Sleeper整個run()方法都是同步的我們可看到與這個對象關聯在一起的Peeker可以正常運行直到我們啟動線程為止隨後Peeker便會完全停止這正是堵塞的一種形式因為Sleeperrun()是同步的而且一旦線程啟動它就肯定在run()內部方法永遠不會放棄對象鎖定造成Peeker線程的堵塞
  Sleeper通過設置不同步的運行提供了一種解決方案只有change()方法才是同步的所以盡管run()位於sleep()內部Peeker仍然能訪問自己需要的同步方法——read()在這裡我們可看到在啟動了Sleeper線程以後Peeker會持續運行下去
   暫停和恢復
  這個例子接下來的一部分引入了掛起或者暫停(Suspend)的概述Thread類提供了一個名為suspend()的方法可臨時中止線程以及一個名為resume()的方法用於從暫停處開始恢復線程的執行顯然我們可以推斷出resume()是由暫停線程外部的某個線程調用的在這種情況下需要用到一個名為Resumer(恢復器)的獨立類演示暫停/恢復過程的每個類都有一個相關的恢復器如下所示
  ///:Continuing
  /////////// Blocking via suspend() ///////////
  class SuspendResume extends Blockable {
   public SuspendResume(Container c) {
   super(c);
   new Resumer(this);
   }
  }
  class SuspendResume extends SuspendResume {
   public SuspendResume(Container c) { super(c);}
   public synchronized void run() {
   while(true) {
   i++;
   update();
   suspend(); // Deprecated in Java
   }
   }
  }
  class SuspendResume extends SuspendResume {
   public SuspendResume(Container c) { super(c);}
   public void run() {
   while(true) {
   change();
   suspend(); // Deprecated in Java
   }
   }
   public synchronized void change() {
   i++;
   update();
   }
  }
  class Resumer extends Thread {
   private SuspendResume sr;
   public Resumer(SuspendResume sr) {
   thissr = sr;
   start();
   }
   public void run() {
   while(true) {
   try {
   sleep();
   } catch (InterruptedException e){}
   srresume(); // Deprecated in Java
   }
   }
  } ///:Continued
  SuspendResume也提供了一個同步的run()方法同樣地當我們啟動這個線程以後就會發現與它關聯的Peeker進入堵塞狀態等候對象鎖被釋放但那永遠不會發生和往常一樣這個問題在SuspendResume裡得到了解決它並不同步整個run()方法而是采用了一個單獨的同步change()方法
  對於Java 大家應注意suspend()和resume()已獲得強烈反對因為suspend()包含了對象鎖所以極易出現死鎖現象換言之很容易就會看到許多被鎖住的對象在傻乎乎地等待對方這會造成整個應用程序的凝固盡管在一些老程序中還能看到它們的蹤跡但在你寫自己的程序時無論如何都應避免本章稍後就會講述正確的方案是什麼
   等待和通知
  通過前兩個例子的實踐我們知道無論sleep()還是suspend()都不會在自己被調用的時候解除鎖定需要用到對象鎖時請務必注意這個問題在另一方面wait()方法在被調用時卻會解除鎖定這意味著可在執行wait()期間調用線程對象中的其他同步方法但在接著的兩個類中我們看到run()方法都是同步在wait()期間Peeker仍然擁有對同步方法的完全訪問權限這是由於wait()在掛起內部調用的方法時會解除對象的鎖定
  我們也可以看到wait()的兩種形式第一種形式采用一個以毫秒為單位的參數它具有與sleep()中相同的含義暫停這一段規定時間區別在於在wait()中對象鎖已被解除而且能夠自由地退出wait()因為一個notify()可強行使時間流逝
  第二種形式不采用任何參數這意味著wait()會持續執行直到notify()介入為止而且在一段時間以後不會自行中止
  wait()和notify()比較特別的一個地方是這兩個方法都屬於基礎類Object的一部分不象
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19649.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.