按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
};
start。addActionListener(new StartL());
add(start);
stopPeekers。addActionListener(
new StopPeekersL());
add(stopPeekers);
}
class StartL implements ActionListener {
public void actionPerformed(ActionEvent e) {
if(!started) {
started = true;
for(int i = 0; i 《 b。length; i++)
b'i'。start();
}
}
}
class StopPeekersL implements ActionListener {
public void actionPerformed(ActionEvent e) {
// Demonstration of the preferred
// alternative to Thread。stop():
for(int i = 0; i 《 b。length; i++)
b'i'。stopPeeker();
}
}
public static void main(String'' args) {
517
…………………………………………………………Page 519……………………………………………………………
Blocking applet = new Blocking();
Frame aFrame = new Frame(〃Blocking〃);
aFrame。addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System。exit(0);
}
});
aFrame。add(applet; BorderLayout。CENTER);
aFrame。setSize(350;550);
applet。init();
applet。start();
aFrame。setVisible(true);
}
} ///:~
在 init()中,注意循环会遍历整个数组,并为页添加state 和peeker。status 文本字段。
首次创建好 Blockable 线程以后,每个这样的线程都会自动创建并启动自己的Peeker。所以我们会看到各个
Peeker 都在Blockable 线程启动之前运行起来。这一点非常重要,因为在 Blockable 线程启动的时候,部分
Peeker 会被堵塞,并停止运行。弄懂这一点,将有助于我们加深对“堵塞”这一概念的认识。
14。3。2 死锁
由于线程可能进入堵塞状态,而且由于对象可能拥有“同步”方法——除非同步锁定被解除,否则线程不能
访问那个对象——所以一个线程完全可能等候另一个对象,而另一个对象又在等候下一个对象,以此类推。
这个“等候”链最可怕的情形就是进入封闭状态——最后那个对象等候的是第一个对象!此时,所有线程都
会陷入无休止的相互等待状态,大家都动弹不得。我们将这种情况称为“死锁”。尽管这种情况并非经常出
现,但一旦碰到,程序的调试将变得异常艰难。
就语言本身来说,尚未直接提供防止死锁的帮助措施,需要我们通过谨慎的设计来避免。如果有谁需要调试
一个死锁的程序,他是没有任何窍门可用的。
1。 Java 1。2对 stop(),suspend(),resume() 以及destroy()的反对
为减少出现死锁的可能,Java 1。2 作出的一项贡献是“反对”使用 Thread 的 stop(),suspend(),resume()
以及destroy()方法。
之所以反对使用 stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯
状态(“被破坏”),那么其他线程能在那种状态下检查和修改它们。结果便造成了一种微妙的局面,我们
很难检查出真正的问题所在。所以应尽量避免使用 stop(),应该采用 Blocking。java 那样的方法,用一个标
志告诉线程什么时候通过退出自己的run()方法来中止自己的执行。
如果一个线程被堵塞,比如在它等候输入的时候,那么一般都不能象在Blocking。java 中那样轮询一个标
志。但在这些情况下,我们仍然不该使用 stop(),而应换用由Thread 提供的 interrupt()方法,以便中止并
退出堵塞的代码。
//: Interrupt。java
// The alternative approach to using stop()
// when a thread is blocked
import java。awt。*;
import java。awt。event。*;
import java。applet。*;
class Blocked extends Thread {
public synchronized void run() {
try {
wait(); // Blocks
518
…………………………………………………………Page 520……………………………………………………………
} catch(InterruptedException e) {
System。out。println(〃InterruptedException〃);
}
System。out。println(〃Exiting run()〃);
}
}
public class Interrupt extends Applet {
private Button
interrupt = new Button(〃Interrupt〃);
private Blocked blocked = new Blocked();
public void init() {
add(interrupt);
interrupt。addActionListener(
new ActionListener() {
public
void actionPerformed(ActionEvent e) {
System。out。println(〃Button pressed〃);
if(blocked == null) return;
Thread remove = blocked;
blocked = null; // to release it
remove。interrupt();
}
});
blocked。start();
}
public static void main(String'' args) {
Interrupt applet = new Interrupt();
Frame aFrame = new Frame(〃Interrupt〃);
aFrame。addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System。exit(0);
}
});
aFrame。add(applet; BorderLayout。CENTER);
aFrame。setSize(200;100);
applet。init();
applet。start();
aFrame。setVisible(true);
}
} ///:~
Blocked。run() 内部的wait()会产生堵塞的线程。当我们按下按钮以后,blocked (堵塞)的句柄就会设为
null,使垃圾收集器能够将其清除,然后调用对象的interrupt()方法。如果是首次按下按钮,我们会看到
线程正常退出。但在没有可供“杀死”的线程以后,看到的便只是按钮被按下而已。
suspend()和resume() 方法天生容易发生死锁。调用 suspend()的时候,目标线程会停下来,但却仍然持有在
这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被“挂起”的线程恢复运行。对任何
线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成令人难堪的死锁。所
以我们不应该使用suspend()和 resume(),而应在自己的Thread 类中置入一个标志,指出线程应该活动还是
挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个
notify()重新启动线程。我们可以修改前面的Counter2。java 来实际体验一番。尽管两个版本的效果是差不
519
…………………………………………………………Page 521……………………………………………………………
多的,但大家会注意到代码的组织结构发生了很大的变化——为所有“听众”都使用了匿名的内部类,而且
Thread 是一个内部类。这使得程序的编写稍微方便一些,因为它取消了 Counter2。java 中一些