ExecutorService是JDK并发工具包提供的一个核心接口,相当于一个线程池,提供执行任务和管理生命周期的方法。ExecutorService接口中的大部分API都是比较容易上手使用的,本文主要介绍下invokeAll和invokeAll方法的特性和使用。

package tasks;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit; public class SleepSecondsCallable implements Callable<String>
{
private String name; private int seconds; public SleepSecondsCallable(String name, int seconds)
{
this.name = name;
this.seconds = seconds;
} public String call() throws Exception
{
System.out.println(name + ",begin to execute"); try
{
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e)
{
System.out.println(name + " was disturbed during sleeping.");
e.printStackTrace();
return name + "_SleepSecondsCallable_failed";
} System.out.println(name + ",success to execute"); return name + "_SleepSecondsCallable_succes";
} }

这是一个通过睡眠来模拟的耗时任务,该任务是可中断/可终止的任务,能够响应中断请求。

package tasks;

import java.util.concurrent.Callable;

public class ExceptionCallable implements Callable<String>
{ private String name = null; public ExceptionCallable()
{ } public ExceptionCallable(String name)
{
this.name = name;
} @Override
public String call() throws Exception
{
System.out.println("begin to ExceptionCallable."); System.out.println(name.length()); System.out.println("end to ExceptionCallable."); return name;
} }

这是一个可能会在执行过程中,抛出空指针异常的任务。

package tasks;

import java.util.Random;
import java.util.concurrent.Callable; public class RandomTenCharsTask implements Callable<String>
{ @Override
public String call() throws Exception
{
System.out.println("RandomTenCharsTask begin to execute..."); StringBuffer content = new StringBuffer(); String base = "abcdefghijklmnopqrstuvwxyz0123456789"; Random random = new Random(); for (int i = 0; i < 10; i++)
{
int number = random.nextInt(base.length());
content.append(base.charAt(number));
} System.out.println("RandomTenCharsTask complete.result=" + content);
return content.toString();
} }

这是一个正常的短时的任务,产生10个随机字符组成的字符串。

一、测试invokeAny()

/**
* 提交的任务集合,一旦有1个任务正常完成(没有抛出异常),会终止其他未完成的任务
*/
public static void invokeAny1() throws Exception
{
ExecutorService executorService = Executors.newFixedThreadPool(3); List<Callable<String>> tasks = new ArrayList<Callable<String>>(); tasks.add(new SleepSecondsCallable("t1", 2));
tasks.add(new SleepSecondsCallable("t2", 1)); String result = executorService.invokeAny(tasks); System.out.println("result=" + result); executorService.shutdown();
}

程序的执行结果是:返回t2线程的执行结果t2_SleepSecondsCallable_succes,同时t1抛出java.lang.InterruptedException: sleep interrupted。

  也就说:一旦有1个任务正常完成(执行过程中没有抛异常),线程池会终止其他未完成的任务
 
第二种情况,向线程池提交3个异常任务ExceptionCallable
/**
* 没有1个正常完成的任务,invokeAny()方法抛出ExecutionException,封装了任务中元素的异常
*
*/
public static void invokeAny2() throws Exception
{
ExecutorService executorService = Executors.newFixedThreadPool(3); List<Callable<String>> tasks = new ArrayList<Callable<String>>(); tasks.add(new ExceptionCallable());
tasks.add(new ExceptionCallable());
tasks.add(new ExceptionCallable()); String result = executorService.invokeAny(tasks); System.out.println("result=" + result); executorService.shutdown();
}

程序执行结果是:调用invokeAny()报错 java.util.concurrent.ExecutionException: java.lang.NullPointerException。

 也就是说:如果提交的任务列表中,没有1个正常完成的任务,那么调用invokeAny会抛异常,究竟抛的是哪儿个任务的异常,无关紧要
 
第三种情况:先提交3个异常任务,再提交1个正常的耗时任务
/**
* 有异常的任务,有正常的任务,invokeAny()不会抛异常,返回最先正常完成的任务
*/
public static void invokeAny3() throws Exception
{
ExecutorService executorService = Executors.newFixedThreadPool(3); List<Callable<String>> tasks = new ArrayList<Callable<String>>(); tasks.add(new ExceptionCallable());
tasks.add(new ExceptionCallable());
tasks.add(new ExceptionCallable());
tasks.add(new ExceptionCallable()); tasks.add(new SleepSecondsCallable("t1", 2)); String result = executorService.invokeAny(tasks); System.out.println("result=" + result);
executorService.shutdown();
}

程序执行结果是:不会抛出任何异常,打印出t2任务的返回结果。也就是说:invokeAny()和任务的提交顺序无关,只是返回最早正常执行完成的任务

第四种情况,测试下使用限时版本的invokeAny(),主要功能与不限时版本的差别不大

/**
* 还没有到超时之前,所以的任务都已经异常完成,抛出ExecutionException<br>
* 如果超时前满,还没有没有完成的任务,抛TimeoutException
*/
public static void invokeAnyTimeout() throws Exception
{
ExecutorService executorService = Executors.newFixedThreadPool(3); List<Callable<String>> tasks = new ArrayList<Callable<String>>(); tasks.add(new ExceptionCallable());
tasks.add(new ExceptionCallable());
tasks.add(new ExceptionCallable());
tasks.add(new ExceptionCallable()); String result = executorService.invokeAny(tasks, 2, TimeUnit.SECONDS); System.out.println("result=" + result); executorService.shutdown();
}

程序执行结果是:抛出ExecutionException。这个其实很合理,也很好理解。如果在超时之前,所有任务已经都是异常终止,那就没有必要在等下去了;如果超时之后,仍然有正在运行或等待运行的任务,那么会抛出TimeoutException。

 最后我们来看下,JDK源码中ExecutorService.invokeAny的方法签名和注释
/**
* Executes the given tasks, returning the result
* of one that has completed successfully (i.e., without throwing
* an exception), if any do. Upon normal or exceptional return,
* tasks that have not completed are cancelled.
* The results of this method are undefined if the given
* collection is modified while this operation is in progress.
*
* @param tasks the collection of tasks
* @return the result returned by one of the tasks
* @throws InterruptedException if interrupted while waiting
* @throws NullPointerException if tasks or any of its elements
* are <tt>null</tt>
* @throws IllegalArgumentException if tasks is empty
* @throws ExecutionException if no task successfully completes
* @throws RejectedExecutionException if tasks cannot be scheduled
* for execution
*/
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;

与我们测试结果一致,invokeAny()返回最先正常完成(without throwing exception)的任务直接结果;一旦有任务正常完成或者调用出现异常,线程池都会终止正在运行或等待运行(tasks that have not completed are cancelled)的任务。

二、测试invokeAll()

这个方法相对来说比较好理解,就是执行任务列表中的所有任务,并返回与每个任务对应的Futue。也就是说,任务彼此之间不会相互影响,可以通过future跟踪每一个任务的执行情况,比如是否被取消,是正常完成,还是异常完成,这主要使用Future类提供的API。

public static void testInvokeAll() throws Exception
{
ExecutorService executorService = Executors.newFixedThreadPool(5); List<Callable<String>> tasks = new ArrayList<Callable<String>>();
tasks.add(new SleepSecondsCallable("t1", 2));
tasks.add(new SleepSecondsCallable("t2", 2));
tasks.add(new RandomTenCharsTask());
tasks.add(new ExceptionCallable()); // 调用该方法的线程会阻塞,直到tasks全部执行完成(正常完成/异常退出)
List<Future<String>> results = executorService.invokeAll(tasks); // 任务列表中所有任务执行完毕,才能执行该语句
System.out.println("wait for the result." + results.size()); executorService.shutdown(); for (Future<String> f : results)
{
// isCanceled=false,isDone=true
System.out.println("isCanceled=" + f.isCancelled() + ",isDone="
+ f.isDone()); // ExceptionCallable任务会报ExecutionException
System.out.println("task result=" + f.get());
}
}

程序的执行结果和一些结论,已经直接写在代码注释里面了。invokeAll是一个阻塞方法,会等待任务列表中的所有任务都执行完成。不管任务是正常完成,还是异常终止,Future.isDone()始终返回true。通过Future.isCanceled()可以判断任务是否在执行的过程中被取消。通过Future.get()可以获取任务的返回结果,或者是任务在执行中抛出的异常。

第二种情况,测试限时版本的invokeAll(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)

/**
* 可以通过Future.isCanceled()判断任务是被取消,还是完成(正常/异常)<br>
* Future.isDone()总是返回true,对于invokeAll()的调用者来说,没有啥用
*/
public static void testInvokeAllTimeout() throws Exception
{
ExecutorService executorService = Executors.newFixedThreadPool(5); List<Callable<String>> tasks = new ArrayList<Callable<String>>();
tasks.add(new SleepSecondsCallable("t1", 2));
tasks.add(new SleepSecondsCallable("t2", 2));
tasks.add(new SleepSecondsCallable("t3", 3));
tasks.add(new RandomTenCharsTask()); List<Future<String>> results = executorService.invokeAll(tasks, 1,
TimeUnit.SECONDS); System.out.println("wait for the result." + results.size()); for (Future<String> f : results)
{
System.out.println("isCanceled=" + f.isCancelled() + ",isDone="
+ f.isDone());
} executorService.shutdown(); }
执行结果是: 

wait for the result.4
isCanceled=true,isDone=true
isCanceled=true,isDone=true
isCanceled=true,isDone=true
isCanceled=false,isDone=true

也就是说给定的超时期满,还没有完成的任务会被取消,即Future.isCancelled()返回true;在超时期之前,无论是正常完成还是异常终止的任务,Future.isCancelled()返回false。

第三种情况,测试在等待invokeAll执行完成之前,线程被中断

/**
* 如果线程在等待invokeAll()执行完成的时候,被中断,会抛出InterruptedException<br>
* 此时线程池会终止没有完成的任务,这主要是为了减少资源的浪费.
*/
public static void testInvokeAllWhenInterrupt() throws Exception
{
final ExecutorService executorService = Executors.newFixedThreadPool(5); // 调用invokeAll的线程
Thread invokeAllThread = new Thread() { @Override
public void run()
{
List<Callable<String>> tasks = new ArrayList<Callable<String>>();
tasks.add(new SleepSecondsCallable("t1", 2));
tasks.add(new SleepSecondsCallable("t2", 2));
tasks.add(new RandomTenCharsTask()); // 调用线程会阻塞,直到tasks全部执行完成(正常完成/异常退出)
try
{
List<Future<String>> results = executorService
.invokeAll(tasks);
System.out.println("wait for the result." + results.size());
} catch (InterruptedException e)
{
System.out
.println("I was wait,but my thread was interrupted.");
e.printStackTrace();
} }
}; invokeAllThread.start(); Thread.sleep(200); invokeAllThread.interrupt(); executorService.shutdown(); }

invokeAllThread 线程调用了ExecutorService.invokeAll(),在等待任务执行完成的时候,invokeAllThread被别的线程中断了。这个时候,

ExecutorService.invokeAll()会抛出Java.lang.InterruptedException,任务t1和t2都被终止抛出java.lang.InterruptedException: sleep interrupted。

也就是说一旦ExecutorService.invokeAll()方法产生了异常,线程池中还没有完成的任务会被取消执行

参见:http://blog.csdn.net/baidu_23086307/article/details/51740852

http://blog.csdn.net/lmj623565791/article/details/27250059

ExecutorService.invokeAny()和ExecutorService.invokeAll()的使用剖析的更多相关文章

  1. Executor线程池的简单使用

    我们都知道创建一个线程可以继承Thread类或者实现Runnable接口,实际Thread类就是实现了Runnable接口. 到今天才明白后端线程的作用:我们可以开启线程去执行一些比较耗时的操作,类似 ...

  2. ExecutorService与Executors例子的简单剖析

    对于多线程有了一点了解之后,那么来看看java.lang.concurrent包下面的一些东西.在此之前,我们运行一个线程都是显式调用了Thread的start()方法.我们用concurrent下面 ...

  3. ExecutorService与Executors例子的简单剖析(转)

    对于多线程有了一点了解之后,那么来看看java.lang.concurrent包下面的一些东西.在此之前,我们运行一个线程都是显式调用了 Thread的start()方法.我们用concurrent下 ...

  4. ExecutorService的invokeAny方法注意

    package com.msxf.datasource.thirdpart.service.extface; import java.util.HashSet; import java.util.Li ...

  5. Java并发编程核心方法与框架-ExecutorService的使用

    在ThreadPoolExecutor中使用ExecutorService中的方法 方法invokeAny()和invokeAll()具有阻塞特性 方法invokeAny()取得第一个完成任务的结果值 ...

  6. Android学习笔记之ExecutorService线程池的应用....

    PS:转眼间就开学了...都不知道这个假期到底是怎么过去的.... 学习内容: ExecutorService线程池的应用... 1.如何创建线程池... 2.调用线程池的方法,获取线程执行完毕后的结 ...

  7. ExecutorService 和 NSOperationQueue

    ExecutorService,简化了Android中的并发处理,NSOperationQueue简化了iOS中的并发处理.它们都管理线程池,作用十分相近,下面简单说明一下. 1.ExecutorSe ...

  8. ExecutorService生命周期

    ExecutorService接口继承了Executor接口,定义了一些生命周期的方法 public interface ExecutorService extends Executor { void ...

  9. ExecutorService介绍2

    Thread和ExecutorService的区别 使用Thread,当子线程执行结束后,主线程如果没有其他执行任务,主线程会终止. /** * Created by litao on 15/10/7 ...

随机推荐

  1. Gym 100818I Olympic Parade(位运算)

    Olympic Parade http://acm.hust.edu.cn/vjudge/contest/view.action?cid=101594#problem/I [题意]: 给出N个数,找出 ...

  2. UIImageView旋转任意角度---实现方法

    转自:http://blog.csdn.net/trandy/article/details/6626281 -(UIImageView *) makeRotation:(UIImageView *) ...

  3. php把时间格式化

    如题,把如 2013-6-12 12:00 格式化为 2013-6--12 可以先将时间转换下,然后重新将时间格式化显示: echo date("Y-m-d", strtotime ...

  4. Oracle DataGuard 物理Standby 搭建(下)

    主备库切换 Switchover 一般SWITCHOVER切换都是计划中的切换,特点是在切换后,不会丢失任何的数据,而且这个过程是可逆的,整个DATA GUARD环境不会被破坏,原来DATA GUAR ...

  5. CN今日凌晨出现全部瘫痪的故障,持续近6个小时

    今日凌晨1点左右,所有cn后缀的网站出现无法访问的情况,原因来自于所有的cn域名均无法解析.据国内知名DNS解析商DNSLA称,故障源自CN所使用的根域名授权服务器瘫痪所致,故障一直持续到今天早上7点 ...

  6. Python 删除目录中特定文件

    代码如下,使用了递归: import sys currDir = sys.path[] import os def removeFile(dir,postfix): if os.path.isdir( ...

  7. Mr.Miss

    umbrella please here my ticket number five sorry sir cloakroom Madam Mr. Mrs Miss lady gentleman mal ...

  8. hdu 5428 The Factor 分解质因数

    The Factor  Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://bestcoder.hdu.edu.cn/contests/contest ...

  9. 【转】Dancing Links精确覆盖问题

    原文链接:http://sqybi.com/works/dlxcn/ (只转载过来一部分,全文请看原文,感觉讲得很好~)正文    精确覆盖问题    解决精确覆盖问题    舞蹈步骤    效率分析 ...

  10. HDU 4293 Groups (线性dp)

    OJ题目:click here~~ 题目分析:n个人分为若干组 , 每一个人描写叙述其所在的组前面的人数和后面的人数.求这n个描写叙述中,最多正确的个数. 设dp[ i ] 为前i个人的描写叙述中最多 ...