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

死鎖

2013-11-15 11:39:52  來源: JSP教程 

  由於線程可能進入堵塞狀態而且由於對象可能擁有同步方法——除非同步鎖定被解除否則線程不能訪問那個對象——所以一個線程完全可能等候另一個對象而另一個對象又在等候下一個對象以此類推這個等候鏈最可怕的情形就是進入封閉狀態——最後那個對象等候的是第一個對象!此時所有線程都會陷入無休止的相互等待狀態大家都動彈不得我們將這種情況稱為死鎖盡管這種情況並非經常出現但一旦碰到程序的調試將變得異常艱難
  就語言本身來說尚未直接提供防止死鎖的幫助措施需要我們通過謹慎的設計來避免如果有誰需要調試一個死鎖的程序他是沒有任何竅門可用的
   Java 對stop()suspend()resume()以及destroy()的反對
  為減少出現死鎖的可能Java 作出的一項貢獻是反對使用Thread的stop()suspend()resume()以及destroy()方法
  之所以反對使用stop()是因為它不安全它會解除由線程獲取的所有鎖定而且如果對象處於一種不連貫狀態(被破壞那麼其他線程能在那種狀態下檢查和修改它們結果便造成了一種微妙的局面我們很難檢查出真正的問題所在所以應盡量避免使用stop()應該采用Blockingjava那樣的方法用一個標志告訴線程什麼時候通過退出自己的run()方法來中止自己的執行
  如果一個線程被堵塞比如在它等候輸入的時候那麼一般都不能象在Blockingjava中那樣輪詢一個標志但在這些情況下我們仍然不該使用stop()而應換用由Thread提供的interrupt()方法以便中止並退出堵塞的代碼
  //: Interruptjava
  // The alternative approach to using stop()
  // when a thread is blocked
  import javaawt*;
  import javaawtevent*;
  import javaapplet*;
  class Blocked extends Thread {
   public synchronized void run() {
   try {
   wait(); // Blocks
   } catch(InterruptedException e) {
   Systemoutprintln(InterruptedException);
   }
   Systemoutprintln(Exiting run());
   }
  }
  public class Interrupt extends Applet {
   private Button
   interrupt = new Button(Interrupt);
   private Blocked blocked = new Blocked();
   public void init() {
   add(interrupt);
  interruptaddActionListener(
   new ActionListener() {
   public
   void actionPerformed(ActionEvent e) {
   Systemoutprintln(Button pressed);
   if(blocked == null) return;
   Thread remove = blocked;
   blocked = null; // to release it
   removeinterrupt();
   }
   });
   blockedstart();
   }
   public static void main(String[] args) {
   Interrupt applet = new Interrupt();
   Frame aFrame = new Frame(Interrupt);
   aFrameaddWindowListener(
   new WindowAdapter() {
   public void windowClosing(WindowEvent e) {
   Systemexit();
   }
   });
   aFrameadd(applet BorderLayoutCENTER);
   aFramesetSize();
   appletinit();
   appletstart();
   aFramesetVisible(true);
   }
  } ///:~
  Blockedrun()內部的wait()會產生堵塞的線程當我們按下按鈕以後blocked(堵塞)的句柄就會設為null使垃圾收集器能夠將其清除然後調用對象的interrupt()方法如果是首次按下按鈕我們會看到線程正常退出但在沒有可供殺死的線程以後看到的便只是按鈕被按下而已
  suspend()和resume()方法天生容易發生死鎖調用suspend()的時候目標線程會停下來但卻仍然持有在這之前獲得的鎖定此時其他任何線程都不能訪問鎖定的資源除非被掛起的線程恢復運行對任何線程來說如果它們想恢復目標線程同時又試圖使用任何一個鎖定的資源就會造成令人難堪的死鎖所以我們不應該使用suspend()和resume()而應在自己的Thread類中置入一個標志指出線程應該活動還是掛起若標志指出線程應該掛起便用wait()命其進入等待狀態若標志指出線程應當恢復則用一個notify()重新啟動線程我們可以修改前面的Counterjava來實際體驗一番盡管兩個版本的效果是差不多的但大家會注意到代碼的組織結構發生了很大的變化——為所有聽眾都使用了匿名的內部類而且Thread是一個內部類這使得程序的編寫稍微方便一些因為它取消了Counterjava中一些額外的記錄工作
  //: Suspendjava
  // The alternative approach to using suspend()
  // and resume() which have been deprecated
  // in Java
  import javaawt*;
  import javaawtevent*;
  import javaapplet*;
  
  public class Suspend extends Applet {
   private TextField t = new TextField();
   private Button
   suspend = new Button(Suspend)
   resume = new Button(Resume);
   class Suspendable extends Thread {
   private int count = ;
   private boolean suspended = false;
   public Suspendable() { start(); }
   public void fauxSuspend() {
   suspended = true;
   }
   public synchronized void fauxResume() {
   suspended = false;
   notify();
   }
   public void run() {
   while (true) {
   try {
   sleep();
   synchronized(this) {
   while(suspended)
   wait();
   }
   } catch (InterruptedException e){}
   tsetText(IntegertoString(count++));
   }
   }
   }
   private Suspendable ss = new Suspendable();
   public void init() {
   add(t);
   suspendaddActionListener(
   new ActionListener() {
   public
   void actionPerformed(ActionEvent e) {
   ssfauxSuspend();
   }
   });
   add(suspend);
   resumeaddActionListener(
   new ActionListener() {
   public
   void actionPerformed(ActionEvent e) {
   ssfauxResume();
   }
   });
   add(resume);
   }
   public static void main(String[] args) {
   Suspend applet = new Suspend();
   Frame aFrame = new Frame(Suspend);
   aFrameaddWindowListener(
   new WindowAdapter() {
   public void windowClosing(WindowEvent e){
   Systemexit();
   }
   });
   aFrameadd(applet BorderLayoutCENTER);
   aFramesetSize();
   appletinit();
   appletstart();
   aFramesetVisible(true);
   }
  }
  Suspendable中的suspended(已掛起)標志用於開關掛起或者暫停狀態為掛起一個線程只需調用fauxSuspend()將標志設為true(真)即可對標志狀態的偵測是在run()內進行的就象本章早些時候提到的那樣wait()必須設為同步(synchronized)使其能夠使用對象鎖在fauxResume()中suspended標志被設為false(假)並調用notify()——由於這會在一個同步從句中喚醒wait()所以fauxResume()方法也必須同步使其能在調用notify()之前取得對象鎖(這樣一來對象鎖可由要喚醍的那個wait()使用)如果遵照本程序展示的樣式可以避免使用wait()和notify()
  Thread的destroy()方法根本沒有實現它類似一個根本不能恢復的suspend()所以會發生與suspend()一樣的死鎖問題然而這一方法沒有得到明確的反對也許會在Java以後的版本(版以後)實現用於一些可以承受死鎖危險的特殊場合
  大家可能會奇怪當初為什麼要實現這些現在又被反對的方法之所以會出現這種情況大概是由於Sun公司主要讓技術人員來決定對語言的改動而不是那些市場銷售人員通常技術人員比搞銷售的更能理解語言的實質當初犯下了錯誤以後也能較為理智地正視它們這意味著Java能夠繼續進步即便這使Java程序員多少感到有些不便就我自己來說寧願面對這些不便之處也不願看到語言停滯不前

From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19400.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.