`
夜乡晨
  • 浏览: 37438 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

为什么不鼓励使用 Thread.stop?

阅读更多
为什么不鼓励使用 Thread.stop?

因为它本质上就是不安全的。停止线程会导致解锁它已锁定的所有监视程序(ThreadDeath 异常传播到栈上后即解锁监视程序)。如果这些监视程序前面保护的任何对象处于不一致状态,则其它线程即可能将这些对象视为处于不一致状态。我们将这种对象称为损坏的对象。当线程操作损坏的对象时,可能会产生任何意外的行为。这种行为可能很难觉察,也可能非常明显。与其它未检查的异常不同,ThreadDeath 将悄悄杀掉线程;这样,用户就不会得到其程序已被破坏的警告。这种破坏可能会在实际损坏发生之后的任何时间显示出来,甚至可能会在数小时或数天之后。
难道不能捕获 ThreadDeath 异常并修复被损坏的对象?

从理论上是可行的,但编写正确的多线程代码的任务将会相当复杂。该任务几乎是无法完成的,原因如下:

   1. 线程可能会在几乎任何地方抛出 ThreadDeath 异常。所以必须在此基础上对所有的同步方法和块进行研究。
   2. 线程可能会在清除第一个 ThreadDeath 异常(在 catch 或 finally 子句中)时抛出第二个异常。 因此必须重复清除直到它成功完成。实现这一点的代码将相当复杂。

总之,上述意图是不切实际的。
对于 Thread.stop(Throwable) 又该怎样办?

除了上述所有问题之外,该方法还可能用来产生其目标线程尚不能处理的异常(包括离开该方法线程几乎不可能产生的已检查异常)。例如,下列方法的行为等同于 Java 的 throw 操作,但是绕过了编译器的下列尝试:即保证调用方法已经声明了它可能抛出的所有已检查异常:

static void sneakyThrow(Throwable t) {
        Thread.currentThread().stop(t);
    }

应该用什么来代替 Thread.stop?

大多数使用 stop 的情况都应该用简单修改一些变量以指示目标线程应停止运行的代码所代替。目标线程应该定期检查该变量,并在该变量指示需要它停止运行时以一种合理的方法从其 run 方法中返回(这是 JavaSoft 教程始终推荐的方法)。要确保停止请求的即时通讯,该变量必须是 volatile(迅变)的(或对该变量的访问必须是同步的)。

例如,假定 applet 包含下列 start、stop 和 run 方法:

private Thread blinker;

    public void start() {
        blinker = new Thread(this);
        blinker.start();
    }

    public void stop() {
        blinker.stop();  // 不安全!
    }

    public void run() {
        Thread thisThread = Thread.currentThread();
        while (true) {
            try {
                thisThread.sleep(interval);
            } catch (InterruptedException e){
            }
            repaint();
        }
    }

则可以通过用下列内容替代 applet 的 stop 和 run 方法来避免使用 Thread.stop:

private volatile Thread blinker;

    public void stop() {
        blinker = null;
    }

    public void run() {
        Thread thisThread = Thread.currentThread();
        while (blinker == thisThread) {
            try {
                thisThread.sleep(interval);
            } catch (InterruptedException e){
            }
            repaint();
        }
    }

如何停止已经等待了很长时间(例如等待输入)的线程?

这就是 Thread.interrupt 方法的用途所在。用户可以使用上述“基于状态”的信号机制,但状态改变(前例中为 blinker = null)的后面可以跟 Thread.interrupt 调用以中断等待:

public void stop() {
        Thread moribund = waiter;
        waiter = null;
        moribund.interrupt();
    }

要使用这种技术,关键是任何捕获中断异常并无法立即处理它的方法要重新声明该异常。之所以称为重新声明而不是重抛出,是因为该异常不是总可以重新抛出的。如果捕获 InterruptedException 异常的方法没有声明抛出该(已检查的)异常,则它应该用下列代码“重新中断自己”:

Thread.currentThread().interrupt();

这可以确保 Thread 尽快重新产生 InterruptedException 异常。
如果线程不响应 Thread.interrupt 该怎么办?

有些情况下,可以使用应用程序特定技巧。例如,如果线程在等待已知套接字,则可以关闭该套接字以使线程立即返回。不幸地是,目前尚没有通用的技术。应该说明,对于所有等待线程不响应 Thread.interrupt 的情况,它也不会响应 Thread.stop。 这种情况包括故意的拒绝服务攻击和 thread.stop 与 thread.interrupt 不能正常工作的 I/O 操作。
为什么不鼓励使用 Thread.suspend 和 Thread.resume?

Thread.suspend 从本质上就是易于死锁的。如果目标线程锁定在一个监视程序上,从而在关键系统资源挂起时保护资源,则在目标线程恢复前将没有线程能访问该资源。如果试图恢复目标线程的线程在调用 resume 之前试图锁定该监视程序,即出现死锁。这种死锁通常将自己显示为“冻结”进程。
应该使用什么来代替 Thread.suspend 和 Thread.resume?

与 Thread.stop 一样,最谨慎的方法是让“目标”线程轮询一个指示所需线程状态(活动或挂起)的变量。当需要挂起状态时,线程将使用 Object.wait 进行等待。当线程恢复时,将使用 Object.notify 通知目标线程。

例如,假定 applet 包含下列 mousePressed 事件处理程序,它将触发名为 blinker 的线程的状态:

private boolean threadSuspended;

    Public void mousePressed(MouseEvent e) {
        e.consume();

        if (threadSuspended)
            blinker.resume();
        else
            blinker.suspend();  // 易于死锁!

        threadSuspended = !threadSuspended;
    }

用下列代码替代上述事件处理程序,即可避免使用 Thread.suspend 和 Thread.resume:

public synchronized void mousePressed(MouseEvent e) {
        e.consume();

        threadSuspended = !threadSuspended;

        if (!threadSuspended)
            notify();
    }

然后将下列代码添加到“run loop”中:

synchronized(this) {
                    while (threadSuspended)
                        wait();
                }

wait

public void run() {
        while (true) {
            try {
                Thread.currentThread().sleep(interval);

                synchronized(this) {
                    while (threadSuspended)
                        wait();
                }
            } catch (InterruptedException e){
            }
            repaint();
        }
    }

注意:mousePressed 方法中的 notify 和 run 方法中的 wait 位于 synchronized 块内部。这是语言所要求的,可确保 wait 和 notify 的序列化。从实际应用来说,这消除了导致“挂起”线程丢失 notify 并无限期地保持挂起状态的竞争情况。

尽管 Java 中同步的费用随着平台的成熟不断下降,但它永远不会是免费的。用户可以使用一种简易技巧来删除添加在每一次“run loop”中的同步。前面添加的同步块被一段稍复杂的代码所替代(这段代码只有在实际挂起时才进入同步块):

if (threadSuspended) {
                    synchronized(this) {
                        while (threadSuspended)
                            wait();
                    }
                }

最终的 run 方法为:

public void run() {
        while (true) {
            try {
                Thread.currentThread().sleep(interval);

                if (threadSuspended) {
                    synchronized(this) {
                        while (threadSuspended)
                            wait();
                    }
                }
            } catch (InterruptedException e){
            }
            repaint();
        }
    }

在没有提供显式同步前,threadSuspended 必须为 volatile,以确保挂起请求的快速通讯。
是否可以利用这两种技术产生能安全“停止”或“挂起”的线程?

是,这一点显而易见。但要注意的一个细节是:目标线程可能在另一个线程试图停止它时就已经挂起。如果 stop 方法仅将状态变量 (blinker) 设置为 null,则目标线程将仍然挂起(在监视程序上等待),而不是象它所应该的那样悄悄退出。如果该 applet 被重新启动,则最终可能会出现多个线程在监视程序上等待,从而导致错误行为。

为了解决该问题,stop 方法必须确保目标线程能在挂起时立即恢复。一旦目标线程恢复后,它必须立即识别出自己已被停止并悄悄退出。下面是最终的 run 和 stop 方法:

public void run() {
        Thread thisThread = Thread.currentThread();
        while (blinker == thisThread) {
            try {
                thisThread.sleep(interval);

                synchronized(this) {
                    while (threadSuspended && blinker==thisThread)
                        wait();
                }
            } catch (InterruptedException e){
            }
            repaint();
        }
    }

    public synchronized void stop() {
        blinker = null;
        notify();
    }

如果 stop 方法调用 Thread.interrupt(如前所述),则它不必调用 notify,但仍必须实现同步。这可以确保目标线程不会因为偶然情况错过中断。
对于 Thread.destroy 又该怎么办?

Thread.destroy

来源:http://proxy73.nyist.net/java/javaweb/linuxjava/cjdk1_2-doc-zh/jdk1.2/zh/docs/guide/misc/threadPrimitiveDeprecation.html
从来就没有实现。如果它已经实现,则将易于出现 Thread.suspend 方式的死锁(实际上,它大致等价于 Thread.suspend 而没有后续的 Thread.resume)。我们现在还没有实现它,同时也不鼓励使用它(防止将来实现它)。尽管它易于死锁,但在有些情况下程序宁愿冒死锁危险也不愿立即退出。 方法将抛出 InterruptedException 异常,因此它必须位于 try ... catch 子句内。最好是将它插入到与 sleep 相同的子句中。sleep 后面(而不是前面)应该进行检查,以在线程“恢复”时立即重绘窗口。最终的 run 方法如下所示:
分享到:
评论
1 楼 shenjichao2009 2011-12-08  
大致了解了一下,但是还没有深入理解

相关推荐

    认识Thread和Runnable

    * 当线程调用 stop 方法,即可使线程进入消亡状态,但是由于 stop 方法是不安全的,不鼓励使用,大家可以通过 run 方法里的条件变通实现线程的 stop。 Timer 和 TimerTask 的使用: Timer 是一种定时器工具,用来...

    语言程序设计资料:JAVA游戏制作.doc

    尽管`Thread.stop()`方法可以停止一个线程,但由于可能引发安全问题,已不推荐使用。现代的线程控制通常采用其他机制,如共享变量、中断标志或`InterruptedException`。 学习Java游戏编程不仅涉及技术知识,还需要...

    实践《Effective Java》书中的经验法则示例代码,结合Java源码来理解这些最佳实践,并应用于实际项目。.zip

    9. **尽可能使用`Objects.requireNonNull()`方法检查参数(Item 35)**:这个方法能确保传递的参数不为null,避免空指针异常,提高代码安全性。 10. **避免在`equals()`方法中调用`this.getClass()`或`instanceof`...

    2021-2022计算机二级等级考试试题及答案No.9884.docx

    20. 线程停止:线程可以通过调用`Thread.sleep()`或`join()`方法暂停,也可以通过`interrupt()`请求停止,但不能直接调用`Thread.stop()`,因为这是不安全的。 21. 不能运行的文件扩展名:.TXT文件通常为文本文件,...

    阿里巴巴java开发手册

    - 避免使用`Thread.stop()`和`Thread.yield()`,推荐使用`interrupt()`来停止线程。 7. **日志记录**: - 日志级别应分为DEBUG、INFO、WARN、ERROR,根据实际需求选择合适的级别。 - 异常信息应完整记录,包括...

    阿巴巴里Java开发手册-effective coding

    - 避免使用`Thread.stop()`、`Thread.yield()`等不安全的线程控制方法。 7. **设计模式**: - 推荐使用单例模式,但要避免全局静态变量,可采用枚举方式实现单例。 - 使用工厂方法替代`new`关键字创建对象,便于...

    超级有影响力霸气的Java面试题大全文档

    为什么要有GC?  GC是垃圾收集的意思(Gabage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域...

    android安卓app如何彻底结束进程. 光调用finish()是结束不了程序的,程序中的线程依旧在后台运行.zip

    然而,这种方法同样不鼓励,因为它可能对系统的正常运行产生负面影响。 3. **停止服务(Service)**: 如果App运行了Service,必须显式调用`stopService()`或`stopSelf()`来停止它,否则Service将继续运行,即使...

    Java游戏编程初步

    `Thread`类提供了`start()`来启动线程,`stop()`来停止线程(不推荐使用,因为不安全),以及`sleep()`方法来使当前线程暂停指定的时间。 在Java中,创建线程通常有两种方式: 1. 继承`Thread`类:创建一个新的类,...

    java编程开发规范试题1

    这是因为这些方法可能导致线程死锁或其他不可预测的行为,使用`Thread.sleep(long millis)`替代`Thread.sleep()`以避免潜在的问题。 14. **方法重载判定**:判断方法是否为重载时,仅关注方法名、参数列表(包括...

    SystemC片上系统设计源代码

    3. **进程和行为级建模**:SystemC提供了两种类型的过程——`sc_process_b`和`sc_thread`,分别对应行为级和连续执行的线程。源代码中可能包含不同类型的进程,展示了它们如何并行运行和交互。 4. **接口设计**:在...

    330 Java Tips(330 JAVA 小技巧)

    虽然具体内容没有详细列出,但从描述中我们可以大致了解到这本书涉及了Java的多个方面,并且鼓励用户通过支付一定费用来支持作者的工作,同时也为发展中国家的用户提供免费阅读的机会。接下来,我们将根据这些信息...

Global site tag (gtag.js) - Google Analytics