线程执行者(九)执行者取消一个任务

声明:本文是《 Java 7 Concurrency Cookbook 》的第四章,作者: Javier Fernández González     译者:许巧辉     校对:方腾飞

执行者取消一个任务

当你使用执行者工作时,你不得不管理线程。你只实现Runnable或 Callable任务和把它们提交给执行者。执行者负责创建线程,在线程池中管理它们,当它们不需要时,结束它们。有时候,你想要取消已经提交给执行者 的任务。在这种情况下,你可以使用Future的cancel()方法,它允许你做取消操作。在这个指南中,你将学习如何使用这个方法来取消已经提交给执行者的任务。

准备工作…

这个指南的例子使用Eclipse IDE实现。如果你使用Eclipse或其他IDE,如NetBeans,打开它并创建一个新的Java项目。

如何做…

按以下步骤来实现的这个例子:

1.创建Task类,指定实现Callable接口,并参数化为String类型。实现call()方法,写入一条信息到控制台,并使这个线程在循环中睡眠100毫秒。

[code lang=”java”]

public class Task implements Callable<String> {
@Override
public String call() throws Exception {
while (true){
System.out.printf("Task: Test\n");
Thread.sleep(100);
}
}

[/code]

2.实现示例的主类,创建Main类,实现main()方法。

[code lang=”java”]

public class Main {
public static void main(String[] args) {

[/code]

3. 使用Executors类的newCachedThreadPool()方法创建ThreadPoolExecutor对象。

[code lang=”java”]

ThreadPoolExecutor executor=(ThreadPoolExecutor)Executors.newCachedThreadPool();

[/code]

4.创建Task对象。

[code lang=”java”]

Task task=new Task();

[/code]

5.使用submit()方法提交任务给执行者。

[code lang=”java”]

System.out.printf("Main: Executing the Task\n");
Future<String> result=executor.submit(task);

[/code]

6.使主任务睡眠2秒。

[code lang=”java”]

try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}

[/code]

7.使用通过submit()方法返回的Future对象result的cancel()方法,取消任务的执行。传入true值作为cancel()方法的参数。

[code lang=”java”]

System.out.printf("Main: Canceling the Task\n");
result.cancel(true);

[/code]

8.将isCancelled()方法和isDone()的调用结果写入控制台,验证任务已取消,因此,已完成。

[code lang=”java”]

System.out.printf("Main: Canceled: %s\n",result.isCanceled());
System.out.printf("Main: Done: %s\n",result.isDone());

[/code]

9.使用shutdown()方法结束执行者,写入信息(到控制台)表明程序结束。

[code lang=”java”]

executor.shutdown();
System.out.printf("Main: The executor has finished\n");

[/code]

它是如何工作的…

当你想要取消你已提交给执行者的任务,使用Future接口的cancel()方法。根据cancel()方法参数和任务的状态不同,这个方法的行为将不同:

  • 如果这个任务已经完成或之前的已被取消或由于其他原因不能被取消,那么这个方法将会返回false并且这个任务不会被取消。
  • 如果这个任务正在等待执行者获取执行它的线程,那么这个任务将被取消而且不会开始它的执行。如果这个任务已经正在运行,则视方法的参数情况而定。 cancel()方法接收一个Boolean值参数。如果参数为true并且任务正在运行,那么这个任务将被取消。如果参数为false并且任务正在运行,那么这个任务将不会被取消。

以下截图显示该示例的执行输出:

7

不止这些…

如果你使用Future对象的get()方法来控制一个已被取消的任务,这个get()方法将抛出CancellationException异常。

参见

  • 在第4章,线程执行者中的执行者执行返回结果的任务指南

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: 线程执行者(九)执行者取消一个任务

  • Trackback 关闭
  • 评论 (5)
    • stphen_kang
    • 2013/08/29 9:53上午

    想问下,如果多个任务如何全部取消,如何一次值取消一个呢?
    对多线程很迷茫~~

      • Snway
      • 2013/08/29 12:13下午

      将任务放到List<Future>中,然后遍历这个数列,逐个取消任务。

    • tony
    • 2014/01/02 10:13上午

    楼主,你这个任务之所以能够取消,关键的代码在于Thread.sleep(100);
    如果循环任务中没有这句代码的话,jvm貌似得不到检测中断标志的时间片,因此任务也就无法取消掉了.
    想请楼主解释一下,还有如果想要取消任务而不得不在循环体中加入Thread.sleep(1)这样投机取巧的代码会有jvm性能上的影响吗?

    • test
    • 2014/04/23 5:07下午

    tony :
    楼主,你这个任务之所以能够取消,关键的代码在于Thread.sleep(100);
    如果循环任务中没有这句代码的话,jvm貌似得不到检测中断标志的时间片,因此任务也就无法取消掉了.
    想请楼主解释一下,还有如果想要取消任务而不得不在循环体中加入Thread.sleep(1)这样投机取巧的代码会有jvm性能上的影响吗?

    这个问题得到答案了吗?

      • Snway
      • 2014/04/24 9:28上午

      本示例的任务之所以能取消,确实与Thread.sleep(100)相关,因为Future.cancel()方法,其实是发送一个中断请求,而sleep能够响应中断,因此能达到取消正在执行任务的目的。也就是说只要执行中的任务能够响应中断,便能通过cancel()方法来取消任务的执行,所以不一定要用Thread.sleep(100),诸如对Thread.interrupted()的判断也行。至于jvm性能上的影响,由于没有实践测试过,不敢妄加下定论,但个人觉得,中断状态也应该只是对一个变量的判断,性能上应该不会有什么影响。如果不想以这种方式取消一个任务,也可以通过一个volatile变量来控制。

return top