堵塞狀態是前述四種狀態中最有趣的
值得我們作進一步的探討
線程被堵塞可能是由下述五方面的原因造成的
(
) 調用sleep(毫秒數)
使線程進入
睡眠
狀態
在規定的時間內
這個線程是不會運行的
(
) 用suspend()暫停了線程的執行
除非線程收到resume()消息
否則不會返回
可運行
狀態
(
) 用wait()暫停了線程的執行
除非線程收到nofify()或者notifyAll()消息
否則不會變成
可運行
(是的
這看起來同原因
非常相象
但有一個明顯的區別是我們馬上要揭示的)
(
) 線程正在等候一些IO(輸入輸出)操作完成
(
) 線程試圖調用另一個對象的
同步
方法
但那個對象處於鎖定狀態
暫時無法使用
亦可調用yield()(Thread類的一個方法)自動放棄CPU
以便其他線程能夠運行
然而
假如調度機制覺得我們的線程已擁有足夠的時間
並跳轉到另一個線程
就會發生同樣的事情
也就是說
沒有什麼能防止調度機制重新啟動我們的線程
線程被堵塞後
便有一些原因造成它不能繼續運行
下面這個例子展示了進入堵塞狀態的全部五種途徑
它們全都存在於名為Blocking
java的一個文件中
但在這兒采用散落的片斷進行解釋(大家可注意到片斷前後的
Continued
以及
Continuing
標志
利用第
章介紹的工具
可將這些片斷連結到一起)
首先讓我們看看基本的框架
//: Blocking
java
// Demonstrates the various ways a thread
// can be blocked
import java
awt
*;
import java
awt
event
*;
import java
applet
*;
import java
io
*;
//////////// The basic framework ///////////
class Blockable extends Thread {
private Peeker peeker;
protected TextField state = new TextField(
);
protected int i;
public Blockable(Container c) {
c
add(state);
peeker = new Peeker(this
c);
}
public synchronized int read() { return i; }
protected synchronized void update() {
state
setText(getClass()
getName()
+
state: i =
+ i);
}
public void stopPeeker() {
// peeker
stop(); Deprecated in Java
peeker
terminate(); // 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) {
c
add(status);
this
b = b;
start();
}
public void terminate() { stop = true; }
public void run() {
while (!stop) {
status
setText(b
getClass()
getName()
+
Peeker
+ (++session)
+
; value =
+ b
read());
try {
sleep(
);
} catch (InterruptedException e){}
}
}
} ///:Continued
Blockable類打算成為本例所有類的一個基礎類
一個Blockable對象包含了一個名為state的TextField(文本字段)
用於顯示出對象有關的信息
用於顯示這些信息的方法叫作update()
我們發現它用getClass
getName()來產生類名
而不是僅僅把它打印出來
這是由於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便會完全停止
這正是
堵塞
的一種形式
因為Sleeper
run()是同步的
而且一旦線程啟動
它就肯定在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) {
this
sr = sr;
start();
}
public void run() {
while(true) {
try {
sleep(
);
} catch (InterruptedException e){}
sr
resume(); // 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