AbstractExecutorService源码理解
来源:互联网 发布:java垃圾回收原理 编辑:程序博客网 时间:2024/06/06 00:43
这个类提供 ExecutorService 执行方法的默认实现。此类使用 newTaskFor 返回的 RunnableFuture 实现 submit、invokeAny 和 invokeAll 方法,默认情况下,RunnableFuture 是此包中提供的 FutureTask 类。例如,submit(Runnable) 的实现创建了一个关联 RunnableFuture 类,该类将被执行并返回。子类可以重写 newTaskFor 方法,以返回 FutureTask 之外的 RunnableFuture 实现。
理解源码之前,先来看下这个类:ExecutorCompletionService
,实现了CompletionService接口,这个类将安排那些完成时提交的任务,把它们放置在可使用 take 访问的队列上。该类非常轻便,适合于在执行几组任务时临时使用
它将异步任务的提交与对结果的获取解耦,生产者提交任务,消费者在任务完成后处理,CompletionService可以处理异步I/O任务,执行读操作的任务作为程序或系统的一部分提交,
然后,当完成读操作时,会在程序的不同部分执行其他操作,执行操作的顺序可能与所请求的顺序不同。
通常,CompletionService 依赖于一个单独的 Executor 来实际执行任务,在这种情况下,CompletionService 只管理一个内部完成队列。
这个是里面的核心的部分,维护一个内部的队列:
/** * FutureTask extension to enqueue upon completion */ 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; } public Future<V> submit(Callable<V> task) { if (task == null) throw new NullPointerException(); RunnableFuture<V> f = newTaskFor(task); executor.execute(new QueueingFuture(f)); return f; }
在submit任务时,使用QueueingFuture包装后提交给线程池执行,其中QueueingFuture继承自FutureTask,重写了done()方法,在执行完成后,调用completionQueue.add(task);将任务加入队列。这样在外部就可以通过poll 或者 take 来获取任务结果了。这种设计算是一种装饰模式的应用了。
我们再回来看AbstractExecutorService,其它方法很简单,之前的博客已将讲过了。主要阅读下invokeAny 和 invokeAll即可。
先看下invokeAny:
/** * the main mechanics of invokeAny. */ private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks, boolean timed, long nanos) throws InterruptedException, ExecutionException, TimeoutException { if (tasks == null) throw new NullPointerException(); int ntasks = tasks.size(); if (ntasks == 0) throw new IllegalArgumentException(); ArrayList<Future<T>> futures = new ArrayList<Future<T>>(ntasks); ExecutorCompletionService<T> ecs = new ExecutorCompletionService<T>(this); // For efficiency, especially in executors with limited // parallelism, check to see if previously submitted tasks are // done before submitting more of them. This interleaving // plus the exception mechanics account for messiness of main // loop. try { // Record exceptions so that if we fail to obtain any // result, we can throw the last exception we got. ExecutionException ee = null; final long deadline = timed ? System.nanoTime() + nanos : 0L; Iterator<? extends Callable<T>> it = tasks.iterator(); // Start one task for sure; the rest incrementally futures.add(ecs.submit(it.next())); --ntasks; int active = 1; for (;;) { Future<T> f = ecs.poll(); if (f == null) { if (ntasks > 0) { --ntasks; futures.add(ecs.submit(it.next())); ++active; } else if (active == 0) break; else if (timed) { f = ecs.poll(nanos, TimeUnit.NANOSECONDS); if (f == null) throw new TimeoutException(); nanos = deadline - System.nanoTime(); } else f = ecs.take(); } if (f != null) { --active; try { return f.get(); } catch (ExecutionException eex) { ee = eex; } catch (RuntimeException rex) { ee = new ExecutionException(rex); } } } if (ee == null) ee = new ExecutionException(); throw ee; } finally { for (int i = 0, size = futures.size(); i < size; i++) futures.get(i).cancel(true); } }
在做完基本的判空后,首选的处理为:
ExecutorCompletionService<T> ecs = new ExecutorCompletionService<T>(this);
ExecutorCompletionService 前文已经说明,装饰了AbstractExecutorService,使其持有了返回结果的队列的功能。之后将返回结果Future加入到ArrayList<Future<T>> futures中,这个为了之后的回收使用的。进入循环,
Future<T> f = ecs.poll();
ecs从内部队列中取出一个返回结果, 如果不是null, 那么return f.get(); 返回计算结果。否则,表明当前内部队列为空,还没有Future对象呢,一条条分析:如果ntasks > 0,表明还有未放入ecs线程池的task,执行futures.add(ecs.submit(it.next()));提交到线程池中执行。active == 0,表明已经没有在运行中的task了,跳出循环。如果timed,表明有等待时间,则在nanos的等待时间内取结果。如果不是timed,则ecs.take()阻塞等待去结果。最后在finally中,将之前加入futures中的task取消执行。
再看invokeAll:
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException { if (tasks == null) throw new NullPointerException(); ArrayList<Future<T>> futures = new ArrayList<Future<T>>(tasks.size()); boolean done = false; try { for (Callable<T> t : tasks) { RunnableFuture<T> f = newTaskFor(t); futures.add(f); execute(f); } for (int i = 0, size = futures.size(); i < size; i++) { Future<T> f = futures.get(i); if (!f.isDone()) { try { f.get(); } catch (CancellationException ignore) { } catch (ExecutionException ignore) { } } } done = true; return futures; } finally { if (!done) for (int i = 0, size = futures.size(); i < size; i++) futures.get(i).cancel(true); } }
实例化ArrayList<Future<T>> futures, 作为之后承载返回结果的对象。
for (Callable<T> t : tasks) { RunnableFuture<T> f = newTaskFor(t); futures.add(f); execute(f); }
遍历tasks,futures.add(f),然后调用execute执行,当然,execute方法由子类实现。
for (int i = 0, size = futures.size(); i < size; i++) { Future<T> f = futures.get(i); if (!f.isDone()) { try { f.get(); } catch (CancellationException ignore) { } catch (ExecutionException ignore) { } } }
这块作用最关键是f.get(),这个方法会阻塞一直等到计算完成返回结果为止,如果执行出错,这个方法会抛出异常,这里将异常吃掉了,之后外部获取的时候不会抛出任何异常了。最后在finally中,判断如果运行有错的话,将剩余的任务cancel掉。
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException { if (tasks == null) throw new NullPointerException(); long nanos = unit.toNanos(timeout); ArrayList<Future<T>> futures = new ArrayList<Future<T>>(tasks.size()); boolean done = false; try { for (Callable<T> t : tasks) futures.add(newTaskFor(t)); final long deadline = System.nanoTime() + nanos; final int size = futures.size(); // Interleave time checks and calls to execute in case // executor doesn't have any/much parallelism. for (int i = 0; i < size; i++) { execute((Runnable)futures.get(i)); nanos = deadline - System.nanoTime(); if (nanos <= 0L) return futures; } for (int i = 0; i < size; i++) { Future<T> f = futures.get(i); if (!f.isDone()) { if (nanos <= 0L) return futures; try { f.get(nanos, TimeUnit.NANOSECONDS); } catch (CancellationException ignore) { } catch (ExecutionException ignore) { } catch (TimeoutException toe) { return futures; } nanos = deadline - System.nanoTime(); } } done = true; return futures; } finally { if (!done) for (int i = 0, size = futures.size(); i < size; i++) futures.get(i).cancel(true); } }
前面的部分和之前的无超时的 invokeAll 方法逻辑相同,主要看下:
for (int i = 0; i < size; i++) { Future<T> f = futures.get(i); if (!f.isDone()) { if (nanos <= 0L) return futures; try { f.get(nanos, TimeUnit.NANOSECONDS); } catch (CancellationException ignore) { } catch (ExecutionException ignore) { } catch (TimeoutException toe) { return futures; } nanos = deadline - System.nanoTime(); } }
从futures中取出一个future,然后调用 f.get(nanos, TimeUnit.NANOSECONDS)等待计算结果, 完成后会更新计算超时时间,nanos = deadline - System.nanoTime();这句表明这个方法的超时时间是所有tasks共用的超时时间,看代码才发现这个知识点。
- AbstractExecutorService源码理解
- Java源码阅读之AbstractExecutorService
- Java Thread&Concurrency(12): 深入理解AbstractExecutorService及其实现原理
- java.util.concurrent.AbstractExecutorService
- Executor实现----AbstractExecutorService实现分析
- java.util.concurrent.AbstractExecutorService.java
- BASH源码理解1
- AsyncQueryHandler 源码理解
- python源码理解
- TLD源码理解
- VCL源码理解
- HOG 理解与源码
- TLD源码理解
- 压缩感知源码理解
- java--ArrayList源码理解
- netty源码理解
- EventBus源码的理解
- 深入源码理解HashMap
- poj 1207 The 3n + 1 problem
- 个人用的潘多拉大杂烩插件教程
- C语言FLOAT类型
- Unit2.11 文档
- Mybatis中example类的使用
- AbstractExecutorService源码理解
- Flask中SqlAlchemy的读写分离配置
- 线段树—hdu1166
- uva1149(贪心)
- [李景山php] 利用redis 作为session的处理程序
- C++实验五—数组分离
- HttpUrl --post请求数据
- java向EXCEL文件中写入数据使用jxljar包
- 本地更改网页