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();

// or

future.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方法,例如:

社区网格信息志愿者招募

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方法是私有的。你必须复制粘贴它。

麦戴尔的奇迹

唯一的选择是不要依赖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 task

runnable.cancel();

cancellableFutures.get(runnable).cancel(true);

(不使用runnable作为键,您可以使用在用例中有意义的标识符,并将runnable和future作为值存储在该键下)

这是一个巧妙的解决方法,但是无论如何,我已经提交了一个增强java.util.concurrent包的请求,所以在未来的版本中,我们可以选择管理这个用例。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值