of检查它的类型是否合适
当你想取消一个正在运行的executor任务时,这个用例并不罕见。例如,您想要停止正在进行的下载,或者您想要取消正在进行的文件复制。所以你有:
|
1
2
3
4
5
6
7
8
9
10
11
|
ExecutorService executor = Executors.newSingleThreadExecutor(); Future<?> future = executor.submit(new Runnable() { @Override public void run() { // Time-consuming or possibly blocking I/O }});....executor.shutdownNow();// orfuture.cancel(); |
不幸的是,这不起作用。打电话shutdownNow()或者cancel()不会停止正在进行的运行。这些方法只是简单地调用.interrupt()在各自的螺纹上。问题是,您的runnable不能处理InterruptedException(而且也不能)。这是许多书籍和文章中描述的一个非常常见的问题,但仍然有点违反直觉。
那你是做什么的?您需要一种方法来停止缓慢或阻塞的操作。如果你有一个长的/无限的循环,你可以只添加一个条件是否Thread.currentThread().isInterrupted()如果是的话就不要继续了。然而,通常情况下,阻塞发生在代码之外,所以您必须指示底层代码停止。通常这是通过关闭流或断开连接来实现的。但是为了做到这一点,你需要做很多事情。
- 扩展
Runnable - 使“可取消的”资源(例如输入流)成为实例字段
- 提供一个
cancel方法添加到您的扩展runnable,在这里您获得“可取消的”资源并取消它(例如调用inputStream.close()) - 实施一种习俗
ThreadFactory这反过来创造了习惯Thread重写interrupt()方法并调用cancel()方法在您的扩展Runnable - 用自定义线程工厂实例化执行器(静态工厂方法将其作为参数)
- 在中处理阻塞资源的突然关闭/停止/断开
run()方法
坏消息是,您需要在线程工厂中访问特定的可取消runnable。你不能使用instanceof检查它的类型是否合适,因为执行程序将您提交给它们的可运行文件包装在Worker不公开其底层可运行程序的实例。
对于单线程执行器来说,这很简单——只需在最外层的类中保存一个对当前提交的runnable的引用,并在interrupt方法,例如:
https://www.wenjuan.com/s/raUvuaW/
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
private final CancellableRunnable runnable;...runnable = new CancellableRunnable() { private MutableBoolean bool = new MutableBoolean(); @Override public void run() { bool.setValue(true); while (bool.booleanValue()) { // emulating a blocking operation with an endless loop } } @Override public void cancel() { bool.setValue(false); // usually here you'd have inputStream.close() or connection.disconnect() }};ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r) { @Override public void interrupt() { super.interrupt(); r.cancel(); } }; }}); Future<?> future = executor.submit(runnable);...future.cancel(); |
(CancellableRunnable是一个自定义接口,它只定义了cancel()方法)
但是,如果您的执行器必须同时运行多个任务,会发生什么情况呢?如果你想全部取消,那么你可以保留一个已提交的列表CancellableRunnable实例,并在被中断时简单地取消它们。因此,runnables将被取消多次,所以你必须考虑到这一点。
如果你想要精细的控制,例如通过取消特定的期货,那么没有简单的解决方案。你甚至不能扩展ThreadPoolExecutor因为addWorker方法是私有的。你必须复制粘贴它。
https://3g.douban.com/note/828891016/
唯一的选择是不要依赖future.cancel()或者executor.shutdownAll()取而代之的是保留你自己的清单CancellableFuture实例,并将它们映射到相应的未来。因此,每当您想要取消一些(或所有)可运行程序时,您可以反过来做——获取您想要取消的可运行程序,调用.cancel()(如上图),然后获取其对应的Future,也取消它。类似于:
|
1
2
3
4
5
6
7
|
Map<CancellableRunnable, Future<?>> cancellableFutures = new HashMap<>();Future<?> future = executor.submit(runnable);cancellableFutures.put(runnable, future);//now you want to abruptly cancel a particular taskrunnable.cancel();cancellableFutures.get(runnable).cancel(true); |
(不使用runnable作为键,您可以使用在用例中有意义的标识符,并将runnable和future作为值存储在该键下)
这是一个巧妙的解决方法,但是无论如何,我已经提交了一个增强java.util.concurrent包的请求,所以在未来的版本中,我们可以选择管理这个用例。
浙公网安备 33010602011771号