Java Concurrency In Practice声明如下:
线程只能由其所有者中断;主人可以 将线程中断策略的知识封装在一个 适当的取消机制,如关机方法
现在让我们来看看标准FutureTask
实施:
public boolean cancel(boolean mayInterruptIfRunning){
//...
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt(); //Future task may not be an owner,
//yet it interrupts the running thread.
}
//...
}
//...
}
那么,为什么它安全?如果ThreadPoolExecutor
将newTaskFor
实现为
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
这样打电话是否安全:
ExecutorService es;
Callable<Integer> task;
//init es, task
Future<Integer> ft = es.submit(task);
ft.cancel(true); //Attention at this line, thread may be interrupted
如果是ThreadPoolExecutor
,我们不知道线程中断策略,FutureTask
也不知道。然而,我们通过FutureTask
打断它。这有点令人困惑......
答案 0 :(得分:1)
没有人声称它安全&#34;。措辞是&#34;线程应该仅由其所有者&#34;中断,而不是必须。
如果您正在调用cancel(true)
,那么您应该为中断线程可能产生的任何影响做好准备。
答案 1 :(得分:1)
本书的引用是作者对最佳实践的意见,而不是关于线程安全的声明。不幸的是,这本书没有明确区分这两种陈述。
答案 2 :(得分:1)
首先,让我们确保问题是合法的。从书中引用:
因为每个线程都有自己的中断策略,所以除非您知道中断对该线程的意义,否则不应该中断线程。
FutureTask
是否中断底层线程由mayInterruptIfRunning
参数控制。因此FutureTask
没有决定何时中断线程,调用者决定。并且调用者应该知道什么是中断策略,因为她控制执行服务。
另一方面,ThreadPoolExecutor
并未真正记录中断其主题的合同(cancels tasks via Thread#interrupt
除shutdownNow()
除外),因此违反了原则来电者。
我认为ThreadPoolExecutor
合同可以在这方面得到改善。但它的行为很自然。使用中断标志是如何让执行任务知道取消的最简单方法(否则可以将可查询状态的对象传递给任务,这将是繁琐的。)
那么,为什么安全?
在这种情况下,ThreadPoolExecutor
并不关心我们是否中断线程。只有在里面运行的任务可能会关心,而且这些任务都在调用者的控制之下。 ThreadPoolExecutor
确保的唯一两件事是在新任务启动之前清除中断标志,并且在关闭时线程被中断(这不违反原则,因为执行者拥有线程)。