小案例
- 代码
public class ComplateServiceDemo { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(3); CompletionService service = new ExecutorCompletionService(executorService); for (int i=0;i<3;i++) { int index = i; service.submit(()-> { try { int i1 = new Random().nextInt(3); System.out.println(index+ " :"+i1); Thread.sleep(1000+i1); } catch (Exception e) { e.printStackTrace(); } return index; }); } for (int i=0;i<3;i++){ try { System.out.println(service.take().get()); } catch (Exception e) { e.printStackTrace(); } } } }
CompletionService
- Completion完成的意思
- 该接口表示已完成的服务,内部接口方法,
//提交任务的接口 Future<V> submit(Callable<V> task); Future<V> submit(Runnable task, V result); //获取已完成任务的接口,阻塞,非阻塞,阻塞一定的时间 Future<V> take() throws InterruptedException; Future<V> poll(); Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException;
- 实现类只有一个,可以自行扩展。ExecutorCompletionService,此类用于提交任务之后,按照执行完成的时候来获取执行之后的结果
ExecutorCompletionService
- 由于单继承关系,可知,该类适用于,提交任务之后,按照任务执行结束的时间,依次获取结果。
- 怎么按照结束时间,来进行获取的呢???下文揭晓!!!
一、关键属性
- 执行器
- 阻塞队列,这个是干嘛用的呢,之前的疑问就是此处解决,会将完成任务的Future加入到该队列中,相当于队列维持了先进先出的结果,那怎么加进去的呢???
private final Executor executor; private final AbstractExecutorService aes; //已完成任务的队列 private final BlockingQueue<Future<V>> completionQueue;
二、构造方法
- 有参构造方法:
- 初始化执行器
- 创建任务完成对列,用于保存已经完成的任务
public ExecutorCompletionService(Executor executor) { if (executor == null) throw new NullPointerException(); this.executor = executor; this.aes = (executor instanceof AbstractExecutorService) ? (AbstractExecutorService) executor : null; this.completionQueue = new LinkedBlockingQueue<Future<V>>(); }
三、重要方法
1. 提交任务
- 源码
public Future<V> submit(Callable<V> task) { if (task == null) throw new NullPointerException(); //将任务转换成了FutureTask,可以进行获取结果,也可以当成任务进行 RunnableFuture<V> f = newTaskFor(task); //交由执行器会执行任务,任务转成了内部实现的QueueingFuture executor.execute(new QueueingFuture(f)); return f; }
- 为何要转成QueueingFuture,这个对象有什么作用呢???
- QueueingFuture此类,是ComplateService的内部类,继承了FutureTask,那么为何将FutureTask适配成此对象,原因是,实现了FutureTask的钩子方法done,用于在FutureTask执行完成后的一些后续流程,通过此方法可以看出,使用done方法,获取到最早执行的任务Future,然后加入到了completionQueue队列中,原来如此呀
private class QueueingFuture extends FutureTask<Void> { QueueingFuture(RunnableFuture<V> task) { super(task, null); this.task = task; } protected void done() { completionQueue.add(task); } private final Future<V> task; }
2. 获取任务
- 源码,从阻塞队列中获取已经完成的任务
public Future<V> take() throws InterruptedException { return completionQueue.take(); } public Future<V> poll() { return completionQueue.poll(); } public Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException { return completionQueue.poll(timeout, unit); }