Thread.stop和朋友在Java中是否安全?

时间:2009-08-16 02:07:09

标签: java multithreading

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

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

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

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

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

8 个答案:

答案 0 :(得分:19)

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

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

  1. 线程执行不得创建或改变在线程停止时其他线程可能看到的任何状态(即Java对象,类变量,外部资源)。
  2. 在正常执行期间,线程执行不得将notify用于任何其他线程。
  3. 该主题不得startjoin其他主题,或使用stopsuspendresume进行互动。
  4. (上面的术语线程执行涵盖了所有应用程序级代码和线程执行的所有库代码。)

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

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

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

    1. Pattern.compile(...)方法可能会更新已编译的静态缓存 模式,如果他们这样做,他们会(应该)使用互斥量来做。 (实际上,OpenJDK 6.0版本不会缓存模式,但Sun可能会改变这种情况。)
    2. 如果你试图通过在控制线程中编译正则表达式并提供预先实例化的Matcher来避免1),那么正则表达式线程 会改变外部可见状态。
    3. 在第一种情况下,我们可能会遇到麻烦。例如,假设使用HashMap实现缓存,并且在重组HashMap时线程被中断。

      在第二种情况下,我们可以提供Matcher尚未传递给其他线程,提供控制器线程没有停止正则表达式匹配器线程后,尝试使用Matcher

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

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

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

      编辑2

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

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

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

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

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

答案 1 :(得分:10)

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

Take mutex

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

// all consistent now

Release mutex

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

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

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

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

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

答案 2 :(得分:1)

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

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

答案 3 :(得分:0)

一个具体的例子可能对此有所帮助。如果有人可以建议一个很好的替代以下使用停止我会非常感兴趣。重写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");
        }
    }
}

答案 4 :(得分:0)

有一些方法可以使用Thread.stop()相对稳定的w / o泄漏内存或文件描述符(FD在* NIX上特别容易泄漏),但只有在您被迫管理第三方代码时才能依赖它。如果您可以控制代码本身,切勿使用它来实现结果。

如果我沿着w / interrupt()使用Thread.stop,还有一些更糟糕的东西,例如添加自定义日志处理程序来重新抛出陷阱的ThreadDeath,添加unhandleExceltionHandler,运行到你自己的ThreadGroup(通过'em同步)等等。 ..

但这值得一个全新的话题。

  
    

但在这种情况下,Java设计师会告诉你;和     他们对我们的语言更加认真,然后我们任何一方:)

  

只是一个注释:其中很多都是非常无能的

答案 5 :(得分:-1)

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

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

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

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

答案 6 :(得分:-2)

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

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

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

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

由于唯一的危险是stop ed线程可以引用的对象可能已损坏,因此在将clone传递给String之前,只需Thread stop 。如果不存在共享对象,则Thread ed {{1}}之外的程序中已损坏对象的威胁将不再存在。

答案 7 :(得分:-2)

没有安全的方法来杀死一个帖子。

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

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

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

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

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