FutureAndCallable实现异步处理

来源:互联网 发布:火星知乎 编辑:程序博客网 时间:2024/05/06 03:07
import java.util.ArrayList;import java.util.List;import java.util.concurrent.Callable;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;import java.util.concurrent.TimeUnit;public class FutureAndCallable {public static void main(String[] args) {// 1. List<Future>// Future.get()是阻塞的,如果第一个任务处理时间过长,其他任务即使处理完成,也需要等待第一个处理完毕// 如果我们不希望出现因为排在前面的任务阻塞导致后面先完成的任务的结果没有及时获取的情况,那么在调用get方式时,需要将超时时间设置为0// 但需要自己实现排在获取后完成的但排在前面任务的结果// V get (long timeout, TimeUnit unit) throws InterruptedException,// ExecutionException, TimeoutException// 同上面的get功能一样,多了设置超时时间。参数timeout指定超时时间,uint指定时间的单位,在枚举类TimeUnit中有相关的定义。如果计// 算超时,将抛出TimeoutExceptionExecutorService es = Executors.newFixedThreadPool(3);List<Future<Integer>> ls = new ArrayList<Future<Integer>>();System.out.println("Future开始");Future<Integer> future = es.submit(new Callable<Integer>() {public Integer call() throws Exception {System.out.println("Future1开始");Thread.sleep(10000);return 101;}});Future<Integer> future1 = es.submit(new Callable<Integer>() {public Integer call() throws Exception {System.out.println("Future2开始");return 102;}});Future<Integer> future2 = es.submit(new Callable<Integer>() {public Integer call() throws Exception {System.out.println("Future3开始");return 103;}});Future<Integer> future3 = es.submit(new Callable<Integer>() {public Integer call() throws Exception {System.out.println("Future4开始");Thread.sleep(10000);return 104;}});ls.add(future);ls.add(future1);ls.add(future2);ls.add(future3);System.out.println("其他一系列操作");try {for (int i = 0; i < 4; i++) {System.out.println("这是第" + i + "次遍历");try {System.out.println(ls.get(i).get(0, TimeUnit.SECONDS));} catch (Exception e) {continue;}}es.shutdown();} catch (Exception e) {e.printStackTrace();}// 2. CompletionService<Integer> cs = new// ExecutorCompletionService<Integer>(threadpool);// ExecutorCompletionService遇到第一个处理时间过长时,会将后面已经处理完毕的有优先返回,最后返回处理完毕的// ExecutorCompletionService分析:// CompletionService是Executor和BlockingQueue的结合体。// 任务的提交和执行都是委托给Executor来完成。当提交某个任务时,该任务首先将被包装为一个QueueingFuture,// QueueingFuture是FutureTask的一个子类,通过改写该子类的done方法,可以实现当任务完成时,将结果放入到BlockingQueue中。// 而通过使用BlockingQueue的take或poll方法,则可以得到结果。在BlockingQueue不存在元素时,这两个操作会阻塞,一旦有结果加入,则立即返回。
// ExecutorService es = Executors.newCachedThreadPool();// CompletionService<Integer> cs = new// ExecutorCompletionService<Integer>(es);// System.out.println("Future开始");// cs.submit(new Callable<Integer>() {// public Integer call() throws Exception {// System.out.println("Future1开始");// Thread.sleep(10000);// return 101;//// }// });// cs.submit(new Callable<Integer>() {// public Integer call() throws Exception {// System.out.println("Future2开始");// return 102;// }// });// cs.submit(new Callable<Integer>() {// public Integer call() throws Exception {// System.out.println("Future3开始");// return 103;// }// });// cs.submit(new Callable<Integer>() {// public Integer call() throws Exception {// System.out.println("Future4开始");// return 104;// }// });//// System.out.println("正常的主线程");// try {// Thread.sleep(5000);// } catch (InterruptedException e1) {// // TODO Auto-generated catch block// e1.printStackTrace();// }// for (int i = 0; i < 4; i++) {// try {// System.out.println(cs.take().get());// } catch (InterruptedException e) {// // TODO Auto-generated catch block// e.printStackTrace();// } catch (ExecutionException e) {// // TODO Auto-generated catch block// e.printStackTrace();// }// }// es.shutdown();}}



总结:模拟四个任务需要异步处理且带有返回值(Callable()),且这四个任务完成顺序不是submit顺序(Executor),即四个任务可能会出现先提交后完成的情况;

1.  第一种方案:Callable处理任务带有返回值;Executor提交任务;将四个Future放入一个List中;最后将List遍历获 取异步处理返回的值;此方案若在任务完成顺序和任务提交顺序一致时,不会出现读取任务之间的长时间阻塞。即,可能任务一还未完成,但任务二已经完成,但在遍历List时,如果任务一未完成,list.get(0).get()会出现阻塞。其原因是因为Future的get()方法就是阻塞方法。因此,如果先提交的任务更耗时,获取异步处理返回值时会出现长时间阻塞,影响性能。

此时,可以通过Future的带参get(long timeout, TimeUnit unit)方法设置超时时间,如果任务提交时间超过了timeout,则会抛出TimeoutException。可以通过这个机制,进行处理;不过与方法2相比较,就显得过于复杂。


2. 第二种方案:CompletionService是Executor和BlockingQueue的结合体。任务的提交和执行都交给了Executor来完成。当提交某个任务时,该任务首先被包装为一个QueueingFuture。QueueingFuture是FutureTask的一个子类,通过改写该子类的done方法,可以实现当任务完成时,将结果放入到BlockingQueue中(ExecutorCompletionService内部实现了LinkedBlockingQueue)。而通过使用BlockingQueue的take或poll方法,则可以得到结果。在BlockingQueue不存在元素时,这两个操作会阻塞,一旦有结果加入,则立即返回,即ExecutorCompletionService会优先返回完成的任务。