《Runnable、Callable、Future和FutureTask之一:获取线程的返回值》
《CompletionService之一:获得线程(线程池的)处理结果》
使用线程池的时候,经常会有需要获得线程处理结果的时候。此时我们通常有两种做法:
1. 使用并发容器将callable.call() 的返回Future存储起来。然后使用一个消费者线程去遍历这个并发容器,调用Future.isDone()去判断各个任务是否处理完毕。然后再处理响应的业务。
2. 使用jdk 自带线程池结果管理器:ExecutorCompletionService。它将BlockingQueue 和Executor 封装起来。然后使用ExecutorCompletionService.submit()方法提交任务。
示例1:用BlockingQueue存放结果:
package com.transsnet.palmpay.cfront.test; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; public class ExecutorResultManager { public static void main(String[] args) { // 队列 BlockingQueue<Future<String>> futures = new LinkedBlockingQueue<>(); // 生产者 new Thread() { @Override public void run() { ExecutorService pool = Executors.newCachedThreadPool(); for (int i=0; i< 10; i++) { int index = i; Future<String> submit = pool.submit(new Callable<String>() { @Override public String call() throws Exception { return "task done" + index; } }); try { futures.put(submit); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); System.out.println("---------------------"); System.out.println(futures.size()); // 消费者 new Thread() { @Override public void run() { while(true) { Future<String> future = futures.poll(); if(future != null && future.isDone()) { // 处理业务 try { System.out.println("消費者" + future.get()); } catch (Exception e) { e.printStackTrace(); } } } } }.start(); } }
结果:
---------------------
0
消費者task done0
消費者task done1
消費者task done2
消費者task done3
消費者task done4
消費者task done5
消費者task done6
消費者task done7
消費者task done8
消費者task done9
示例2:用普通的list存放结果:
public class ExecutorResultManager2 { public static void main(String[] args) { // 队列 List<Future<String>> futures = new ArrayList<>(); // 生产者 new Thread() { @Override public void run() { ExecutorService pool = Executors.newCachedThreadPool(); for (int i=0; i< 10; i++) { int index = i; Future<String> submit = pool.submit(new Callable<String>() { @Override public String call() throws Exception { TimeUnit.SECONDS.sleep(5); return "task done" + index; } }); futures.add(submit); } System.out.println("--消費者-----------------"); System.out.println(futures.size()); while(futures.size() > 0) { Future<String> future = futures.get(0); //System.out.println("future="+ future); if(future.isDone()) { // 处理业务 try { System.out.println("消費者" + future.get()); futures.remove(0); } catch (Exception e) { e.printStackTrace(); } } } } }.start(); } }
结果:
--消費者-----------------
10
消費者task done0
消費者task done1
消費者task done2
消費者task done3
消費者task done4
消費者task done5
消費者task done6
消費者task done7
消費者task done8
消費者task done9
示例3:
import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; public class ExecutorResultManager3 { public static void main(String[] args) { final Random r = new Random(); // 创建一个固定大小的线程池 ExecutorService es = Executors.newFixedThreadPool(10); // 将所有处理结果提交到一个固定大小的队列(可不指定,默认创建一个无界队列) ExecutorCompletionService<String> ecs = new ExecutorCompletionService<String>( es,new LinkedBlockingQueue<Future<String>>(1000)); for (int i = 0; i < 10; i++) { //提交所有任务 ecs.submit(new Callable<String>() { @Override public String call() throws Exception { //模拟一个耗时操作 long l = r.nextInt(4000); Thread.sleep(l); return Thread.currentThread().getName() + "|" + l; } }); try { //获得返回结果,3s超时(表示我们能够容忍的最大等待时间) System.out.println(System.currentTimeMillis() + ecs.take().get(3, TimeUnit.SECONDS)); } catch (Exception e) { e.printStackTrace(); } } es.shutdown(); } }
结果:
1617599671725pool-1-thread-1|2967
1617599674695pool-1-thread-2|2380
1617599677076pool-1-thread-3|3308
1617599680385pool-1-thread-4|399
1617599680786pool-1-thread-5|1060
1617599681847pool-1-thread-6|2106
1617599683955pool-1-thread-7|1566
1617599685521pool-1-thread-8|819
1617599686342pool-1-thread-9|903
1617599687246pool-1-thread-10|3417