关闭

多线程之Executor框架

332人阅读 评论(0) 收藏 举报
分类:

Executor框架是指JAVA 5中引入的java.util.concurrent包中的一系列功能类。包括了Executor,Executors,ExecutorService,AbstractExecutorService,Future,Callable,Runnable等。如下图:


一:Executor

Executor作为顶层接口,只提供了一个执行任务的方法。

<span style="font-size:14px;">void execute(Runnable command)</span>

二:ExecutorService

ExecutorService扩展了Executor并添加了Executor声明周期管理的方法。一个Executor的生命周期有三种状态,运
行(Running),关闭(Shutdown),终止(Terminate)。当Executor创建时处于运行状态,当调用
ExecutorService.shutdown()后,处于关闭状态,当Executor中所有的线程执行结束后,Executor处于终止状态。
ExecutorService中的主要方法:

1.  void shutdown()

调用该方法后,Executor处于关闭状态,不再接受新任务,如果此时还往Executor中添加任务,则抛类似与下面的异
常: java.util.concurrent.RejectedExecutionException。但允许执行之前提交但在等待队列中尚未执行的的任务,待所有的任务执行完成后,Executor处于终止状态。

2.  List<Runnable>  shutdownNow()

与shutdown功能类似,区别在于调用shutdownNow方法会尝试去停止正在执行的任务(但并不保证一定会立即停
止),并且返回调用该方法之前之前提交的尚未执行的任务列表。

3.  boolean isShutdown()

判断Executor是否关闭,当调用Executor.shutdown()或shutdownNow()后,该方法返回true。

4.  boolean isTerminated()

判断Executor是否终止,当调用Executor.shutdown()或shutdownNow(),待所有线程执行完成后,Executor处于终止
状态,调用该方法返回true。

5. boolean awaitTermination(long timeout, TimeUnit unit)   throws InterruptedException;

判断Executor在指定的时间内是否终止,如果超时则默认为未终止。timeout指定时间,unit指定时间单位。

6. <T> Future<T> submit(Callable<T> task)

向Executor中提交一个任务执行,任务实现Callable接口,并异步返回任务的执行结果Future<T>

7. <T> Future<T> submit(Runnable task, T result)

向Executor中提交一个任务执行,task为实现Runable接口的任务, resule指定任务的执行结果类型,并返回任务的

执行结果Future<T>

8. Future<?> submit(Runnable task)

向Executor中提交一个任务执行,默认的返回结果类型为Void

9. <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)  throws

InterruptedException;

向Executor中提交一系列任务,全部调用执行,并返回与每个任务对应的Future,将执行结果返回到一个List列表
中。也就是说,任务彼此之间不会相互影响,可以通过future跟踪每一个任务的执行情况

10. <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,  long timeout, 
TimeUnit unit)   throws InterruptedException

功能和上面的类似,只不过指定了一定的时间内获取结果,如果在指定的时间内没有执行完,则抛出
TimeoutExecption,中止任务线程。

11. <T> T invokeAny(Collection<? extends Callable<T>> tasks)   throws InterruptedException, ExecutionException;

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

12. <T> T invokeAny(Collection<? extends Callable<T>> tasks,  long timeout, TimeUnit unit)   throws InterruptedException, ExecutionException, TimeoutException;

与上面的功能类似,不过也指定了在一定的时间内,如果在指定的时间内没有获取到结果,则抛TimeoutExecption,中止任务线程。

三:ThreadPoolExecutor

ThreadPoolExecutor是各个线程池的底层实现。关于这个类的详细介绍请参考:多线程之ThreadPoolExecutor

四:Executors

Executors是一个工具类,提供了很多方法:创建线程池,创建Callable,创建ThreadFactory等。

Executors的主要方法:

1. public static ExecutorService newFixedThreadPool(int nThreads) 

创建一个线程数固定的线程池(ExecutorService)

<span style="font-size:14px;">public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
   }</span>

2. public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)

功能与newFixedThreadPool()相同,区别在于线程有指定的线程工厂类创建

<span style="font-size:14px;">public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }</span>

3. public static ExecutorService newSingleThreadExecutor() 

创建只拥有一个线程的线程池

<span style="font-size:14px;">public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }</span>

4. public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)

功能与newSingleThreadExecutor()相同,区别在于线程有指定的线程工厂类创建

<span style="font-size:14px;">public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }</span>

5. public static ExecutorService newCachedThreadPool() 

创建线程数可动态调整的线程池,线程的空闲时间为60s,超过60s则回收该线程,无上限。

<span style="font-size:14px;">public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }</span>

6. public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)

功能与newCachedThreadPool()相同,区别在于线程有指定的线程工厂类创建

<span style="font-size:14px;">public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }</span>

7. public static ScheduledExecutorService newSingleThreadScheduledExecutor()

创建只有一个线程的定时任务线程池,可定时执行任务

<span style="font-size:14px;">public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
    }</span>

8. public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)

功能与newSingleThreadScheduledExecutor()相同,区别在于线程有指定的线程工厂类创建

<span style="font-size:14px;">public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1, threadFactory));
    }</span>

9.  public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

创建一个指定线程数的定时任务线程池,详细介绍参考章节:多线程之ScheduledExecutorService

<span style="font-size:14px;">public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }</span>

10. public static ScheduledExecutorService newScheduledThreadPool( int corePoolSize, ThreadFactory threadFactory) 

功能与newScheduledThreadPool()相同,区别在于线程有指定的线程工厂类创建

<span style="font-size:14px;">public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }</span>

11. public static ThreadFactory defaultThreadFactory() 

返回一个默认的线程工厂类

<span style="font-size:14px;">public static ThreadFactory defaultThreadFactory() {
        return new DefaultThreadFactory();
    }</span>
DefaultThreadFactory类源码:

<span style="font-size:14px;">static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }</span>

12. public static <T> Callable<T> callable(Runnable task, T result)

由Runnable接口和执行结果的返回值类型创建一个Callable类返回

13. public static Callable<Object> callable(Runnable task)

有Runnable接口和默认的类型Void创建一个Callable类返回

五:线程池使用

任务类:
<span style="font-size:14px;">public class Task implements Runnable {

	private String name;
	
	int count = 0;

	public Task(String name) {
		this.name = name;
	}

	@Override
	public void run() {
		while (count < 4) {
			count++;
			System.out.println(Thread.currentThread().getName() + "执行任务" + name+ "中。。。");
			try {
				Thread.sleep(1000 * 1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}</span>

线程工厂类
<span style="font-size:14px;">public class WorkThreadFactory implements ThreadFactory{

	private static AtomicInteger count = new AtomicInteger(0);
	
	@Override
	public Thread newThread(Runnable r) {
		return new Thread(r, "线程"+ count.addAndGet(1));
	}
}</span>

1. CacheThreadPool使用

主程序:
<span style="font-size:14px;">public class MainThread {

	private static WorkThreadFactory factory = new WorkThreadFactory();//线程工厂
	
	private static ExecutorService cacheThreadPool = Executors.newCachedThreadPool(factory);
	
	private static ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2, factory);
	
	private static ExecutorService singleExecutor = Executors.newSingleThreadExecutor(factory);
	
	private static ScheduledExecutorService sheduleExecutor = Executors.newScheduledThreadPool(2, factory);
	
	private static ScheduledExecutorService singleThreadscheduleExecutor = Executors.newSingleThreadScheduledExecutor(factory);
 
	public static void main(String[] args) {
		for(int i=0; i<3; i++){
			cacheThreadPool.execute(new Task(String.valueOf(i)));
		}
	}
}</span>

运行结果:

线程2执行任务1中。。。
线程1执行任务0中。。。
线程3执行任务2中。。。
线程2执行任务1中。。。
线程1执行任务0中。。。
线程3执行任务2中。。。
线程2执行任务1中。。。
线程1执行任务0中。。。
线程3执行任务2中。。。
线程2执行任务1中。。。
线程1执行任务0中。。。
线程3执行任务2中。。。

从执行结果可以看出,这3个任务有3个线程在执行。

2. FixedThreadPool使用

依旧使用上面的程序,只是更换下线程池

<span style="font-size:14px;">public static void main(String[] args) {
		for(int i=0; i<3; i++){
			fixedThreadPool.execute(new Task(String.valueOf(i)));
		}
	}</span>

运行结果:

线程1执行任务0中。。。
线程2执行任务1中。。。
线程1执行任务0中。。。
线程2执行任务1中。。。
线程1执行任务0中。。。
线程2执行任务1中。。。
线程1执行任务0中。。。
线程2执行任务1中。。。
线程1执行任务2中。。。
线程1执行任务2中。。。
线程1执行任务2中。。。
线程1执行任务2中。。。


从运行结果可知,虽然有3个任务要运行,但是只有2个线程在执行这两个任务,因为配置该线程时就配置了该线程池线程的个数为固定2个。

3. SingleExecutor的使用

依旧使用上面的程序,只是更换下线程池

<span style="font-size:14px;">public static void main(String[] args) {
		for(int i=0; i<3; i++){
			singleExecutor.execute(new Task(String.valueOf(i)));
		}
	}</span>
运行结果:

线程1执行任务0中。。。
线程1执行任务0中。。。
线程1执行任务0中。。。
线程1执行任务0中。。。
线程1执行任务1中。。。
线程1执行任务1中。。。
线程1执行任务1中。。。
线程1执行任务1中。。。
线程1执行任务2中。。。
线程1执行任务2中。。。
线程1执行任务2中。。。
线程1执行任务2中。。。

从运行结果可知,只有一个线程在执行所有的任务。

4. ScheduledExecutorService的使用

请参考章节:多线程之ScheduledExecutorService

5. SingleScheduledExecutorService的使用

请参考章节:多线程之ScheduledExecutorService

六:ExecutorService的shutdown()与shutdownNow()

在本文中的第二部分已经讲到过shutdown()与shutdownNow()的区别,下面主要是从实例角度来分析下,依旧使用上面的程序,用FixedThreadPool线程池

1. 用shutdown()停止

public static void main(String[] args) {
		for(int i=0; i<3; i++){
			fixedThreadPool.execute(new Task(String.valueOf(i)));
		}
		if(!fixedThreadPool.isShutdown()){
			System.out.println("++++++++线程池处于运行状态+++++++++++++++++");
		}
		fixedThreadPool.shutdown();
		
		while(!fixedThreadPool.isShutdown()){
		}
		
		System.out.println("********线程池处于关闭状态****************");
		
		while(!fixedThreadPool.isTerminated()){
		}
		
		System.out.println("=========线程池处于终止状态===============");
		
	}

运行结果:

++++++++线程池处于运行状态+++++++++++++++++
********线程池处于关闭状态****************
线程2执行任务1中。。。
线程1执行任务0中。。。
线程2执行任务1中。。。
线程1执行任务0中。。。
线程2执行任务1中。。。
线程1执行任务0中。。。
线程2执行任务1中。。。
线程1执行任务0中。。。
线程2执行任务2中。。。
线程2执行任务2中。。。
线程2执行任务2中。。。
线程2执行任务2中。。。
=========线程池处于终止状态===============

从运行结果可看出,当ExecutorService.shutdown被调用后,ExecutorService就处于关闭状态,但是当前正在运行的2个任务并没有被中止,而此时任务2应该是在等待执行的队列中,因为配的线程池为FixedThreadPool,大小为2,并发同时只能执行两个任务。在执行完任务0,任务1之后,任务2也被执行了,说明ExecutorService虽然被shutdown了,但是ExecutorService依然会执行已经被提交了但处于等待执行队列中的任务。

2. 用shutdownNow()停止

<span style="font-family:System;">public static void main(String[] args) {
		for(int i=0; i<3; i++){
			fixedThreadPool.execute(new Task(String.valueOf(i)));
		}
		if(!fixedThreadPool.isShutdown()){
			System.out.println("++++++++线程池处于运行状态+++++++++++++++++");
		}
		fixedThreadPool.shutdownNow();//改换成shutdownNow()

		System.out.println("********线程池处于关闭状态****************");
		
		while(!fixedThreadPool.isTerminated()){
		}
		
		System.out.println("=========线程池处于终止状态===============");
		
	}
}</span>

运行结果:

++++++++线程池处于运行状态+++++++++++++++++
********线程池处于关闭状态****************
线程1执行任务0中。。。
线程1执行任务0中。。。
线程2执行任务1中。。。
线程2执行任务1中。。。
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at cn.test.executor.Task.run(Task.java:26)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at cn.test.executor.Task.run(Task.java:26)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
线程1执行任务0中。。。
线程2执行任务1中。。。
线程1执行任务0中。。。
线程2执行任务1中。。。
=========线程池处于终止状态===============

从运行结果可看出,当ExecutorService.shutdownNow被调用后,ExecutorService就处于关闭状态,但是当前正在运行的2个任务并没有被中止,而此时任务2也是在等待执行的队列中,在执行完任务0,任务1之后,任务2没被执行就结束了,说明ExecutorService被shutdownNow()后,ExecutorService不会再执行之前被提交了但处于等待执行队列中的任务。

3. 用shutdown()停止后继续添加任务
<span style="white-space:pre">	</span>public static void main(String[] args) {
		for(int i=0; i<3; i++){
			fixedThreadPool.execute(new Task(String.valueOf(i)));
		}
		if(!fixedThreadPool.isShutdown()){
			System.out.println("++++++++线程池处于运行状态+++++++++++++++++");
		}
		fixedThreadPool.shutdown();

		for(int i=0; i<3; i++){//继续添加任务
			fixedThreadPool.execute(new Task(String.valueOf(i)));
		}
		
		System.out.println("********线程池处于关闭状态****************");
		
		while(!fixedThreadPool.isTerminated()){
		}
		
		System.out.println("=========线程池处于终止状态===============");
		
	}

运行结果:

Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task cn.test.executor.Task@4aad8dbc rejected from java.util.concurrent.ThreadPoolExecutor@483457f1[Shutting down, pool size = 2, active threads = 2, queued tasks = 1, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2048)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:821)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1372)
at cn.test.executor.MainThread.main(MainThread.java:36)
....

从运行结果中可看出,当ExecutorService调用shutdown之后,继续往ExecutorSevice中添加任务就会报异常:RejectedExecutionException

4. 用shutdownNow()停止后继续添加任务

结果与测试3一样

七:ExecutorService的invokeAny()与invokeAll()

在本文中的第二部分也讲到了invokeAny与invokeAll()的作用,现在主要将下这两个方法的用法。主要是参考了网上一博友的文章:ExecutorServer中invokeAny与invokeAll的使用剖析

<span style="font-family:System;font-size:10px;">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";
	}

}</span><span style="font-size:18px;font-family: FangSong_GB2312;">
</span>
这是一个通过睡眠来模拟的耗时任务,该任务是可中断/可终止的任务,能够响应中断请求。
<span style="font-family:System;font-size:10px;">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;
	}
}</span>
这是一个可能会在执行过程中,抛出空指针异常的任务。
<span style="font-family:System;font-size:10px;">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();
	}
}</span>
这是一个正常的短时的任务,产生10个随机字符组成的字符串。

1.测试invokeAny()

第一种情况,向线程池提交2个耗时任务SleepSecondsCallable
<span style="font-family:System;font-size:10px;">/**
	 * 提交的任务集合,一旦有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();
	}</span>
程序的执行结果是:返回t2线程的执行结果t2_SleepSecondsCallable_succes,同时t1抛出java.lang.InterruptedException: sleep interrupted。
也就说:一旦有1个任务正常完成(执行过程中没有抛异常),线程池会终止其他未完成的任务

第二种情况,向线程池提交3个异常任务ExceptionCallable
<span style="font-family:System;font-size:10px;">/**
	 * 没有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("a"));
		String result = executorService.invokeAny(tasks);
		System.out.println("result=" + result);
		executorService.shutdown();
	}</span>
程序执行结果是:调用invokeAny()报错 java.util.concurrent.ExecutionException: java.lang.NullPointerException。
也就是说:如果提交的任务列表中,没有1个正常完成的任务,那么调用invokeAny会抛异常,究竟抛的是哪儿个任务的异常,无关紧要

第三种情况:先提交3个异常任务,再提交1个正常的耗时任务

<span style="font-family:System;font-size:10px;">/**
	 * 有异常的任务,有正常的任务,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();
	}</span>
程序执行结果是:不会抛出任何异常,打印出t2任务的返回结果。也就是说:invokeAny()和任务的提交顺序无关,只是返回最早正常执行完成的任务

第四种情况,测试下使用限时版本的invokeAny(),主要功能与不限时版本的差别不大
<span style="font-family:System;font-size:10px;">/**
	 * 还没有到超时之前,所以的任务都已经异常完成,抛出ExecutionException<br>
	 * 如果超时前满,还没有没有完成的任务,抛TimeoutException
	 */
	public static void invokeAny4() 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();
	}</span>
程序执行结果是:抛出ExecutionException。这个其实很合理,也很好理解。如果在超时之前,所有任务已经都是异常终止,那就没有必要在等下去了;如果超时之后,仍然有正在运行或等待运行的任务,那么会抛出TimeoutException。

最后我们来看下,JDK源码中ExecutorService.invokeAny的方法签名和注释
<span style="font-family:System;font-size:10px;">/**
     * 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;</span>
与我们测试结果一致,invokeAny()返回最先正常完成(without throwing exception)的任务直接结果;一旦有任务正常完成或者调用出现异常,线程池都会终止正在运行或等待运行(tasks that have not completed are cancelled)的任务。

2.测试invokeAll()

这个方法相对来说比较好理解,就是执行任务列表中的所有任务,并返回与每个任务对应的Futue。也就是说,任务彼此之间不会相互影响,可以通过future跟踪每一个任务的执行情况,比如是否被取消,是正常完成,还是异常完成,这主要使用Future类提供的API。
<span style="font-family:System;font-size:10px;">public static void invokeAll1() 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());
		}
	}</span>
程序的执行结果和一些结论,已经直接写在代码注释里面了。invokeAll是一个阻塞方法,会等待任务列表中的所有任务都执行完成。不管任务是正常完成,还是异常终止,Future.isDone()始终返回true。通过Future.isCanceled()可以判断任务是否在执行的过程中被取消。通过Future.get()可以获取任务的返回结果,或者是任务在执行中抛出的异常。


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

<span style="font-family:System;font-size:10px;">/**
	 * 可以通过Future.isCanceled()判断任务是被取消,还是完成(正常/异常)<br>
	 * Future.isDone()总是返回true,对于invokeAll()的调用者来说,没有啥用
	 */
	public static void invokeAll2() 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());
			System.out.println("task result=" + f.get());
		}
		executorService.shutdown();
	}</span>
执行结果是:

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执行完成之前,线程被中断。

<span style="font-family:System;font-size:10px;">/**
	 * 如果线程在等待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();
	}</span>
invokeAllThread 线程调用了ExecutorService.invokeAll(),在等待任务执行完成的时候,invokeAllThread被别的线程中断了。这个时候,ExecutorService.invokeAll()会抛出java.lang.InterruptedException,任务t1和t2都被终止抛出java.lang.InterruptedException: sleep interrupted。也就是说一旦ExecutorService.invokeAll()方法产生了异常,线程池中还没有完成的任务会被取消执行。











0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:108638次
    • 积分:2060
    • 等级:
    • 排名:第18695名
    • 原创:73篇
    • 转载:130篇
    • 译文:8篇
    • 评论:7条
    文章分类