简介
看到这个标题的时候,我也很纳闷,因为没有明白所表达的意思。
ok,并发编程比较常见的一个问题是:当采用多个并发任务来解决一个问题的时候,往往只需要关心这个任务的第一个结果,例如:验证一个算法的时候,假如一个执行5个算法,那么最先返回结果的,就是最快的。
在本章将会学习,如何使用ThreadPoolExecutor来实现类似场景;
———-4.5 运行多个任务并处理所有结果
以下说明总结是后面一章的浓缩,因为没有什么可讲的了,就是一个方法的使用:
等待任务结束的方法有:
1. Future.isDone()判断任务状态,
2. Future.get() 会阻塞直到任务结束
3. ThreadPoolExecutor.shutdown(); 关闭执行器,等待所有任务结束。(纠正一下:该方法只是发起一个有序的关闭,并且不接受新任务),它能和awaitTermination(1, TimeUnit.DAYS)方法配合能达到阻塞并等待所有任务结束,在4.7中有详细示例
上面的方法各有缺点:
1. 只能判断任务的状态
2. 只能对一个任务阻塞
3. 只能通过关闭执行器和awaitTermination等待所有任务的结束
所以4.5 运行多个任务并处理所有结果 章节中的方法就是使用invokeAll方法来等待一组任务的结束。其实大家可能都想到了,肯定是封装上面的单个处理。后面一章就不单独开章节了。
本章ThreadPoolExecutor使用心得
- 使用 ThreadPoolExecutor.invokeAny(list); 让线程池来帮我们拿到最快返回结果的结果。//执行给定的任务,如果某个任务已成功完成(也就是未抛出异常),则返回其结果。一旦正常或异常返回后,则取消尚未完成的任务。如果此操作正在进行时修改了给定的 collection,则此方法的结果是不确定的。
- jdk 中说明了:任务结束(完成)的标识是:未抛出异常,正常返回;
- 拿到第一个结果后,执行器会取消未完成的任务,而在多线程中:当线程在活动之前或活动期间处于正在等待、休眠或占用状态且该线程被中断时,抛出InterruptedException异常
- 如果所有任务都抛出了异常,那么最终返回结果的时候也会抛出异常。(抛出的异常按照最后返回task)
- 还有一个invokeAll方法:当所有任务完成时返回所有任务的future列表。
示例
场景描述:以下程序来模拟在数据库中查找两个用户名(根据用户名和密码),获取最快查询到的用户。
/**
* Created by zhuqiang on 2015/8/30 0030.
*/
public class Client {
public static void main(String[] args) {
ArrayList<Task> list = new ArrayList<Task>();
list.add(new Task(new UserValidator(),"小强","123456"));
list.add(new Task(new UserValidator(), "小小强", "123456"));
list.add(new Task(new UserValidator(), "小小小小强", "123456"));
ThreadPoolExecutor es = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
try {
String s = es.invokeAny(list);//执行给定的任务,如果某个任务已成功完成(也就是未抛出异常),则返回其结果。一旦正常或异常返回后,则取消尚未完成的任务。如果此操作正在进行时修改了给定的 collection,则此方法的结果是不确定的。
System.out.println(s + " Main——用户验证通过");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
es.shutdown();
}
}
// 用户验证对象,用来模拟在数据库中查询的过程
class UserValidator{
public boolean validator(String name,String password){
long time = (long)(Math.random() * 10);
try {
TimeUnit.SECONDS.sleep(time);
System.out.println("耗时=========" + Thread.currentThread().getName() + " 耗时:" + time);
} catch (InterruptedException e) {
System.out.println("取消=========" +Thread.currentThread().getName()+ " 该任务被中断");
return false;
}
// return new Random().nextBoolean(); //返回一个随机值。表示是否验证通过的标识
return true; //始终返回true。用来验证,第一个被找到之后,后面的线程是否会被取消任务
}
}
class Task implements Callable<String>{
private UserValidator uv;
private String name;
private String password;
public Task(UserValidator uv, String name, String password) {
this.uv = uv;
this.name = name;
this.password = password;
}
@Override
public String call() throws Exception {
if (!uv.validator(name,password)){ //没有找到,并抛出一个异常
System.out.println("没有找到用户=========" + "Task:" + Thread.currentThread().getName() + " " + name);
throw new Exception("没有找到该用户:" + name);
}
System.out.println("找到用户=========" + "Task:" + Thread.currentThread().getName() + " " + name);
return name;
}
}
某一次运行结果:
耗时=========pool-1-thread-2 耗时:1
找到用户=========Task:pool-1-thread-2 小小强
小小强 Main——用户验证通过
取消=========pool-1-thread-2 该任务被中断
没有找到用户=========Task:pool-1-thread-2 小小小小强
取消=========pool-1-thread-1 该任务被中断
没有找到用户=========Task:pool-1-thread-1 小强
结果说明
1. 可以看到,第一个找到之后,结果就被返回了,并且后面未完成的任务被中断了。
2. 此示例的关键点es.invokeAny(list)
查看如果所有任务都没有返回结果呢,就是都抛出了异常
修改上面的代码,让所有任务都抛出异常,会得到下面的某一次的运行结果:
耗时=========pool-1-thread-1 耗时:3
没有找到用户=========Task:pool-1-thread-1 小强
耗时=========pool-1-thread-1 耗时:0
没有找到用户=========Task:pool-1-thread-1 小小小小强
耗时=========pool-1-thread-2 耗时:6
java.util.concurrent.ExecutionException: java.lang.Exception: 没有找到该用户:小小强
没有找到用户=========Task:pool-1-thread-2 小小强
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at java.util.concurrent.AbstractExecutorService.doInvokeAny(AbstractExecutorService.java:193)
at java.util.concurrent.AbstractExecutorService.invokeAny(AbstractExecutorService.java:215)
at java7Concurrency.sync4_4.Client.main(Client.java:18)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: java.lang.Exception: 没有找到该用户:小小强
at java7Concurrency.sync4_4.Task.call(Client.java:60)
at java7Concurrency.sync4_4.Task.call(Client.java:45)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)