Thread.stop和朋友在Java中安全吗?
java.lang.Thread
中的stop()
, suspend()
和resume()
不推荐使用,因为它们是不安全的 。 Sun推荐的解决方法是使用Thread.interrupt()
,但是这种方法在所有情况下都不起作用。 例如,如果您正在调用不显式或隐式地检查interrupted
标志的库方法,则别无select,只能等待调用完成。
所以,我想知道是否有可能描述在线程上调用stop()
情况(可certificate)。 例如, stop()
一个什么也没做的线程,但在java.util.regex.Matcher
上调用find(...)
或match(...)
是否安全?
(如果有任何Sun工程师正在阅读这篇文章,我们将非常感激。
编辑 :答案,简单地重申,你不应该叫stop()
因为它已被否决,不安全,无论是什么都没有问题的重点。 我知道在大多数情况下这是真正的不安全的,如果有一个可行的select,你应该总是使用它。
这个问题是关于它是安全的子集情况。 具体来说,这个子集是什么?
这是我回答我自己的问题的尝试。
我认为使用Thread.stop()
可以安全地停止下面的条件:
- 线程执行不能创build或改变线程停止时可能对其他线程可见的任何状态(即Java对象,类variables,外部资源)。
- 在正常执行期间,线程执行不能使用
notify
给任何其他线程。 - 该线程不能
start
或join
其他线程,或者使用stop
,suspend
或resume
交互。
(上面的线程执行一词涵盖了线程执行的所有应用程序级代码和所有库代码。)
第一个条件意味着已停止的线程不会将任何外部数据结构或资源置于不一致的状态。 这包括它可能在互斥体中访问(读取)的数据结构。 第二个条件意味着一个可停止的线程不能让其他线程等待。 但是它也禁止使用除简单对象互斥之外的任何同步机制。
一个可阻止的线程必须有一种方法来将每个计算的结果传递给控制线程。 这些结果是由可停止的线程创build/变异的,所以我们只需要确保它们在线程停止后不可见。 例如,可以将结果分配给Thread对象的私有成员,并用一个由线程primefaces地表示“完成”的标志来“守护”。
编辑 :这些条件是相当严格的。 例如,如果我们必须保证正则expression式引擎不会改变任何外部可见状态,那么对于“正则expression式求值程序”线程才能安全停止。 问题是,它可能会做,取决于你如何实现线程!
-
Pattern.compile(...)
方法可能会更新已编译模式的静态caching,如果他们确实会(应该)使用互斥体来执行此操作。 (实际上,OpenJDK 6.0版本不会caching模式,但是Sun可能会改变这一点。) - 如果你试图避免1)通过在控制线程中编译正则expression式并提供一个预先实例化的
Matcher
,那么正则expression式线程会改变外部可见状态。
在第一种情况下,我们可能会遇到麻烦。 例如,假设使用HashMap来实现caching,并且在重组HashMap时线程被中断。
在第二种情况下,如果Matcher
没有被传递给其他线程,并且规定控制器线程在停止正则expression式匹配器线程之后没有尝试使用Matcher
,我们就可以。
那么这个离开我们呢?
那么,我想我已经确定了线程在理论上是安全停止的条件。 我也认为理论上可以静态分析一个线程的代码(以及它所调用的方法),看看这些条件是否始终成立。 但是,我不确定这是否真的很实际。
这有道理吗? 我错过了什么吗?
编辑2
当你考虑到我们可能试图杀死的代码可能是不可信的时,事情会变得更加多毛:
-
我们不能依靠“承诺”; 例如不可信代码上的注释,它可以是可压缩的,也可以是不可压缩的。
-
实际上,我们需要能够阻止不可信的代码,使其不可操作的代码…根据确定的标准。
我怀疑这将需要修改JVM行为(例如,实现运行时限制允许locking或修改哪些线程),或完全实现Isolates JSR。 这超出了我所认为的“公平游戏”的范围。
因此,现在我们来规定不可信的代码案例。 或者至less,承认恶意代码可以使事情变得不安全,并将问题放在一边。
缺乏安全性来自关键部分的构想
Take mutex do some work, temporarily while we work our state is inconsistent // all consistent now Release mutex
如果你吹掉了线程,并且发生在临界区域,那么这个对象将处于不一致的状态,这意味着从这一点开始不能安全使用。
为了杀死线程是安全的,你需要理解在线程中正在执行的任何操作的整个过程,要知道代码中没有这样的关键部分。 如果您正在使用库代码,那么您可能无法看到源代码并知道它是安全的。 即使今天安全,也不一定是明天。
(非常人为的)可能的不安全的例子。 我们有一个链表,它不是循环的。 所有的algorithm都非常灵活,因为我们知道它不是循环的。 在我们的关键部分,我们暂时引入一个循环。 在我们离开临界区之前,我们会被吹走。 现在所有使用列表循环的algorithm都是永久的。 没有图书馆作者肯定会这样做! 你怎么知道的? 你不能认为你使用的代码写得很好。
在你指出的例子中,当然可以以可中断的方式编写requreidfunction。 更多的工作,但可能是安全的。
我将采取传单:没有可用于可取消线程的对象和方法的logging子集,因为没有图书馆作者想要做出保证。
也许有一些我不知道,但作为java.sun.com说,这是不安全的,因为这个线程正在处理的任何东西都有严重的风险被破坏。 其他的对象,连接,打开的文件…出于显而易见的原因,如“不先closures你的Word而不先保存”。
对于这个find(...)
例如,我真的不认为这是一个灾难,简单地踢了一个sutiless .stop()
…
一个具体的例子可能会帮助这里。 如果任何人都可以build议一个很好的替代下面的停止使用,我会非常感兴趣。 重写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"); } } }
有很多方法可以使用Thread.stop()相对稳定的W / O泄漏内存或文件描述符(FD在* NIX上非常容易泄漏),但是只有当您被迫pipe理第三方代码时,才会依赖它。 如果您可以控制代码本身,请不要使用它来实现结果。
如果我沿着w / interrupt()方法使用Thread.stop,并且添加一些更多的黑客东西,例如添加自定义日志处理程序来重新抛出被困的ThreadDeath,添加unhandleExceltionHandler,运行到你自己的ThreadGroup(同步)等。
但这是值得一个全新的话题。
但在这种情况下,Javadevise者告诉你, 而且他们对他们的语言更认真,然后我们:)
只是一个说明:其中不less人是相当无知的
如果我的理解是正确的,则问题与同步locking没有被释放,因为生成的ThreadInterruptedException()传播堆栈。
认为这是理所当然的,因为你永远不知道在stop()被调用和执行的那一刻你碰巧是否有任何“内部方法调用”,这是有效地保持一些同步锁,然后什么Java工程师说,看起来是明确无误的。
我个人不明白的是,为什么不可能释放任何同步锁,因为这种特殊types的exception传播到堆栈,从而传递所有'}'方法/同步块分隔符,这将导致任何锁被释放对于任何其他types的例外。
我有一个用java编写的服务器,如果该服务的pipe理员想要“冷关机”,那么无论如何停止所有正在运行的活动都是必要的。 任何对象状态的一致性不是一个问题,因为我所要做的就是退出。 尽我所能。
没有安全的方法杀死一个线程。
没有一个安全的情况下的子集。 即使在Windows上进行testing的时候也是100%的工作,它可能会破坏Solaris下的JVM进程内存,或者在Linux下泄露线程资源。
一个人应该永远记住,在Java线程下面有一个真实的,原生的,不安全的线程。
该本地线程与本地,低级,数据和控制结构一起工作。 杀死它可能会使这些本机数据结构处于无效状态,无法恢复。
Java机器没有办法考虑所有可能的后果,因为线程不仅可以在JVM进程内分配/使用资源,而且可以在OS内核内分配/使用资源。
换句话说,如果本地线程库没有提供一个安全的方法来kill()一个线程,那么Java就不能提供更好的保证。 而所有已知的本地实现状态,杀死线程是一个危险的业务。
所有forms的并发控制都可以由Java同步原语通过构build更复杂的 并发 控制来适应您的问题。
您提供的链接中清楚地列出了弃用的原因。 如果您愿意接受原因,那么可以随意使用这些function。
但是,如果您select使用这些function,那么您也同意可以随时停止对这些function的支持。
编辑:我会重申贬值的原因以及如何避免它们。
由于唯一的危险是stop
编辑线程可以引用的对象可能会被损坏,只需在将String
传递给Thread
之前clone
该String
即可。 如果不存在共享对象,那么程序外Thread
被破坏的威胁将不再存在。