java并发编程-限时任务

来源:互联网 发布:wampserver域名重定向 编辑:程序博客网 时间:2024/04/28 10:29

原文地址:http://blog.csdn.net/aitangyong/article/details/38172189

ExecutorService是JDK并发工具包提供的一个核心接口,相当于一个线程池,提供执行任务和管理生命周期的方法。ExecutorService接口中的大部分API都是比较容易上手使用的,本文主要介绍下invokeAll和invokeAll方法的特性和使用。我们先提供几个任务类:一个耗时任务,一个异常任务,一个短时任务。他们会在接下来的测试代码中使用。
[java] view plaincopyprint?
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;
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";
}

}

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

[java] view plaincopyprint?
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.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;
}

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

[java] view plaincopyprint?
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();  
     }  
   
 }  
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个随机字符组成的字符串。

1.测试invokeAny()
第一种情况,向线程池提交2个耗时任务SleepSecondsCallable
[java] view plaincopyprint?
/** 
  * 提交的任务集合,一旦有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();  
 }  
/**
 * 提交的任务集合,一旦有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
[java] view plaincopyprint?
/** 
 * 没有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();  
 }  
/**
* 没有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个正常的耗时任务
[java] view plaincopyprint?
/** 
 * 有异常的任务,有正常的任务,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();  
 }  
/**
* 有异常的任务,有正常的任务,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(),主要功能与不限时版本的差别不大
[java] view plaincopyprint?
/** 
  * 还没有到超时之前,所以的任务都已经异常完成,抛出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<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的方法签名和注释
[java] view plaincopyprint?
/** 
      * 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;  
/**
     * 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)的任务。


2.测试invokeAll()
这个方法相对来说比较好理解,就是执行任务列表中的所有任务,并返回与每个任务对应的Futue。也就是说,任务彼此之间不会相互影响,可以通过future跟踪每一个任务的执行情况,比如是否被取消,是正常完成,还是异常完成,这主要使用Future类提供的API。
[java] view plaincopyprint?
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());  
     }  
 }  
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)
[java] view plaincopyprint?
/** 
  * 可以通过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();  
   
 }  
/**
 * 可以通过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执行完成之前,线程被中断。
[java] view plaincopyprint?
/** 
  * 如果线程在等待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();  
   
 }  
/**
 * 如果线程在等待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()方法产生了异常,线程池中还没有完成的任务会被取消执行。

0 0