java菜鸟教程 Thread.stop和朋友在Java中是否安全?




java菜鸟教程 (7)

一个具体的例子可能会有所帮助。 如果有人可以建议一个很好的替代以下使用停止我会非常感兴趣。 重写java.util.regex以支持中断不计算在内。

import java.util.regex.*;
import java.util.*;

public class RegexInterruptTest {

    private static class BadRegexException extends RuntimeException { }
        final Thread mainThread = Thread.currentThread();
        TimerTask interruptTask = new TimerTask() {
            public void run() {
                System.out.println("Stopping thread.");
                // Doesn't work:
                // mainThread.interrupt();
                // Does work but is deprecated and nasty
                mainThread.stop(new BadRegexException());
            }
        };

        Timer interruptTimer = new Timer(true);
        interruptTimer.schedule(interruptTask, 2000L);

        String s = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab";
        String exp = "(a+a+){1,100}";
        Pattern p = Pattern.compile(exp);
        Matcher m = p.matcher(s);
        try {
            System.out.println("Match: " + m.matches());
            interruptTimer.cancel();
        } catch(BadRegexException bre) {
            System.out.println("Oooops");
        } finally {
            System.out.println("All over");
        }
    }
}

不推荐使用java.lang.Thread中的stop()suspend()resume() ,因为它们unsafe 。 Sun推荐的解决方法是使用Thread.interrupt() ,但这种方法并不适用于所有情况。 例如,如果您调用的库方法没有显式或隐式地检查interrupted标志,那么您别无选择,只能等待调用完成。

所以,我想知道是否有可能描述在线程上调用stop() (可证明)安全的情况。 例如, stop()除了在java.util.regex.Matcher上调用find(...)match(...)什么都不做的线程是否安全?

(如果有任何Sun工程师正在阅读这篇文章......我将非常感谢您的确切答案。)

编辑 :简单地重述你不应该调用stop()的咒语的答案,因为它已被弃用,不安全,无论什么都缺少这个问题。 我知道在大多数情况下它确实是不安全的,并且如果有可行的替代方案,你应该总是使用它。

这个问题是关于安全的子集案例。 具体来说,那个子集是什么?


Answer #1

缺乏安全性来自关键部分的想法

Take mutex

do some work, temporarily while we work our state is inconsistent

// all consistent now

Release mutex

如果你吹掉线程并且它发生在关键部分,那么该对象将处于不一致状态,这意味着从该点开始不能安全使用。

为了安全地杀死线程,您需要了解该线程中正在执行的任何操作的整个处理,以便知道代码中没有这样的关键部分。 如果您使用的是库代码,那么您可能无法查看源代码并知道它是安全的。 即使它今天安全,也可能不是明天。

(非常做作)可能不安全的例子。 我们有一个链表,它不是循环的。 所有的算法都非常活泼,因为我们知道它不是循环的。 在我们的关键部分,我们暂时引入一个循环。 然后,在我们从关键部分出现之前,我们会被吹走。 现在所有使用列表的算法永远循环。 没有图书馆作者肯定会这样做! 你怎么知道的? 您不能假设您使用的代码写得很好。

在您指向的示例中,肯定可以以可中断的方式编写requreid功能。 更多的工作,但可能是安全的。

我将采用传单:没有文档的对象和方法子集可以在可取消的线程中使用,因为没有库作者想要做出保证。


Answer #2

这是我尝试回答我自己的问题。

我认为以下条件应该足以使用Thread.stop()安全地停止单个线程:

  1. 线程执行不得创建或改变在线程停止时其他线程可能看到的任何状态(即Java对象,类变量,外部资源)。
  2. 线程执行在正常执行期间不得对任何其他线程使用notify
  3. 线程不能startjoin其他线程,或者使用stopsuspendresume交互。

(上面的线程执行一词涵盖了所有应用程序级代码和线程执行的所有库代码。)

第一个条件意味着停止的线程不会使任何外部数据结构或资源处于不一致状态。 这包括它可能在互斥锁中访问(读取)的数据结构。 第二个条件意味着可停止的线程不能让其他线程等待。 但它也禁止使用任何其他简单对象互斥体的同步机制。

可停止的线程必须有一种方法将每个计算的结果传递给控制线程。 这些结果由可停止的线程创建/变异,因此我们只需要确保它们在线程停止后不可见。 例如,结果可以分配给Thread对象的私有成员,并用一个标志“保护”,该标志由线程原子地说它是“完成”。

编辑 :这些条件相当严格。 例如,对于要安全停止的“正则表达式求值程序”线程,如果我们必须保证正则表达式引擎不会改变任何外部可见状态。 问题是它可能会这样做,具体取决于你如何实现线程!

  1. Pattern.compile(...)方法可能会更新已编译模式的静态缓存,如果他们这样做,他们将(应该)使用互斥锁来执行此操作。 (实际上,OpenJDK 6.0版本不会缓存模式,但Sun可能会改变这种情况。)
  2. 如果你试图避免1)通过在控制线程中编译正则表达式并提供预先实例化的Matcher ,那么正则表达式线程确实会改变外部可见状态。

在第一种情况下,我们可能会遇到麻烦。 例如,假设使用HashMap实现缓存,并且在重组HashMap时线程被中断。

在第二种情况下,如果Matcher没有被传递给其他线程,并且假设控制器线程在停止正则表达式匹配器线程后没有尝试使用Matcher ,那么我们就可以

那么这又给我们留下了什么?

好吧,我想我已经确定了理论上可以安全地停止线程的条件。 我还认为理论上可以静态分析线程的代码(以及它调用的方法),看看这些条件是否总是成立。 但是,我不确定这是否真的很实用。

这有意义吗? 我错过了什么吗?

编辑2

当你考虑到我们可能试图杀死的代码可能不受信任时,事情变得更加毛茸茸:

  1. 我们不能依赖“承诺”; 例如,对于不受信任的代码的注释,它可以是可充电的,也可以是不可投入的。

  2. 实际上,我们需要能够阻止不受信任的代码执行使其无法终止的事情......根据已确定的标准。

我怀疑这将需要修改JVM行为(例如,实现运行时限制允许锁定或修改的线程),或者完全实现Isolates JSR。 这超出了我所考虑的“公平游戏”的范围。

因此,现在让我们排除不受信任的代码案例。 或者至少,承认恶意代码可以做一些事情来使自己不能安全地杀戮,并把这个问题放在一边。


Answer #3

所有形式的并发控制都可以由Java同步原语通过构造更适合您问题的complex concurrency controls来提供。

您提供的链接中明确说明了弃用的原因。 如果您愿意接受原因,请随意使用这些功能。

但是,如果您选择使用这些功能,则还接受可以随时停止对这些功能的支持。

编辑:我将重申弃用的原因以及如何避免它们。

由于唯一的危险是可以被stop ed线程引用的对象可能被破坏,所以只需clone String然后再将其传递给Thread 。 如果不存在共享对象,则stop编辑Thread外的程序中已损坏对象的威胁将不再存在。


Answer #4

也许有一些我不知道的东西,但正如java.sun.com所说,这是不安全的,因为这个线程处理的任何东西都有被破坏的严重风险。 其他对象,连接,打开的文件...出于显而易见的原因,例如“不先保存而不关闭Word”。

对于这个find(...)例子,我真的不认为用一个不起作用的.stop()它踢出来是一场灾难......


Answer #5

如果我的理解是正确的,那么问题与同步锁未被释放有关,因为生成的ThreadInterruptedException()在堆栈中向上传播。

认为这是理所当然的,它本质上是不安全的,因为你永远无法知道你在调用和实现stop()的那一刻是否碰巧遇到任何“内部方法调用”,实际上是持有一些同步锁,然后是什么java工程师说,似乎是非常正确的。

我个人不明白为什么不可能释放任何同步锁,因为这种特殊类型的Exception传播到堆栈,从而传递所有'}'方法/同步块分隔符,这会导致任何锁被释放对于任何其他类型的例外。

我有一个用java编写的服务器,如果该服务的管理员想要“冷关闭”,那么无论如何都能够停止所有正在运行的活动。 任何对象状态的一致性都不是问题,因为我所要做的就是退出。 我尽可能快。


Answer #6

杀死线程没有安全的方法。

没有一部分情况是安全的。 即使它在Windows上进行测试时工作率为100%,也可能会破坏Solaris下的JVM进程内存或泄漏Linux下的线程资源。

人们应该永远记住,在Java Thread下面有一个真实的,原生的,不安全的线程。

该本机线程使用本机,低级,数据和控制结构。 杀死它可能会使这些本机数据结构处于无效状态,而无法恢复。

Java机器无法将所有可能的后果考虑在内,因为线程不仅可以在JVM进程内分配/使用资源,还可以在OS内核中分配/使用资源。

换句话说,如果本机线程库不提供kill()线程的安全方法,那么Java无法提供更好的保证。 我所知道的所有本机实现都声明杀死线程是一项危险的业务。





multithreading