AbstractExecutorService对ExecutorService的执行任务类型的方法提供了一个默认实现。这些方法包括submit,invokeAny和InvokeAll。

注意的是来自Executor接口的execute方法是未被实现,execute方法是整个体系的核心,所有的任务都是在这个方法里被真正执行的,因此该方法的不同实现会带来不同的执行策略。这个在后面分析ThreadPoolExecutor和ScheduledThreadPoolExecutor就能看出来。

首先来看submit方法,它的基本逻辑是这样的:

1. 生成一个任务类型和Future接口的包装接口RunnableFuture的对象

2. 执行任务

3. 返回future。

    public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
} public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}

因为submit支持Callable和Runnable两种类型的任务,因此newTaskFor方法有两个重载方法:

    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
} protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}

上一篇文章里曾经说过Callable和Runnable的区别在于前者带返回值,也就是说Callable=Runnable+返回值。因此java中提供了一种adapter,把Runnable+返回值转换成Callable类型。这点可以在newTaskFor中的FutureTask类型的构造函数的代码中看到:

    public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
sync = new Sync(callable);
} public FutureTask(Runnable runnable, V result) {
sync = new Sync(Executors.callable(runnable, result));
}

以下是Executors.callable方法的代码:

    public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}

那么RunnableAdapter的代码就很好理解了,它是一个Callable的实现,call方法的实现就是执行Runnable的run方法,然后返回那个value。

    static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}

接下来先说说较为简单的invokeAll:

1. 为每个task调用newTaskFor方法生成得到一个既是Task也是Future的包装类对象的List

2. 循环调用execute执行每个任务

3. 再次循环调用每个Future的get方法等待每个task执行完成

4. 最后返回Future的list。

    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException {
if (tasks == null || unit == null)
throw new NullPointerException();
long nanos = unit.toNanos(timeout);
List<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());
boolean done = false;
try {
// 为每个task生成包装对象
for (Callable<T> t : tasks)
futures.add(newTaskFor(t)); long lastTime = System.nanoTime(); // 循环调用execute执行每个方法
// 这里因为设置了超时时间,所以每次执行完成后
// 检查是否超时,超时了就直接返回future集合
Iterator<Future<T>> it = futures.iterator();
while (it.hasNext()) {
execute((Runnable)(it.next()));
long now = System.nanoTime();
nanos -= now - lastTime;
lastTime = now;
if (nanos <= 0)
return futures;
} // 等待每个任务执行完成
for (Future<T> f : futures) {
if (!f.isDone()) {
if (nanos <= 0)
return futures;
try {
f.get(nanos, TimeUnit.NANOSECONDS);
} catch (CancellationException ignore) {
} catch (ExecutionException ignore) {
} catch (TimeoutException toe) {
return futures;
}
long now = System.nanoTime();
nanos -= now - lastTime;
lastTime = now;
}
}
done = true;
return futures;
} finally {
if (!done)
for (Future<T> f : futures)
f.cancel(true);
}
}

最后说说invokeAny,它的难点在于只要一个任务执行成功就要返回,并且会取消其他任务,也就是说重点在于找到第一个执行成功的任务。

这里我想到了BlockingQueue,当所有的任务被提交后,任务执行返回的Future会被依次添加到一个BlockingQueue中,然后找到第一个执行成功任务的方法就是从BlockingQueue取出第一个元素,这个就是doInvokeAny方法用到的ExecutorCompletionService的基本原理。

因为两个invokeAny方法都是调用doInvokeAny方法,下面是doInvokeAny的代码分析:

    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();
List<Future<T>> futures= new ArrayList<Future<T>>(ntasks);
// ExecutorCompletionService负责执行任务,后面调用用poll返回第一个执行结果
ExecutorCompletionService<T> ecs =
new ExecutorCompletionService<T>(this); // 这里出于效率的考虑,每次提交一个任务之后,就检查一下有没有执行完成的任务 try {
ExecutionException ee = null;
long lastTime = timed ? System.nanoTime() : 0;
Iterator<? extends Callable<T>> it = tasks.iterator(); // 先提交一个任务
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();
// 这里的更新不可少,因为这个Future可能是执行失败的情况,那么还需要再次等待下一个结果,超时的设置还是需要用到。
long now = System.nanoTime();
nanos -= now - lastTime;
lastTime = now;
}
// 没有设置超时,并且所有任务都被提交了,则一直等到第一个执行结果出来
else
f = ecs.take();
}
// 有返回结果了,尝试从future中获取结果,如果失败了,那么需要接着等待下一个执行结果
if (f != null) {
--active;
try {
return f.get();
} catch (ExecutionException eex) {
ee = eex;
} catch (RuntimeException rex) {
ee = new ExecutionException(rex);
}
}
} // ExecutorCompletionService执行时发生错误返回了全是null的future
if (ee == null)
ee = new ExecutionException();
throw ee; } finally {
// 尝试取消所有的任务(对于已经完成的任务没有影响)
for (Future<T> f : futures)
f.cancel(true);
}
}

后面接着分析ThreadPoolExecutor和ScheduledThreadPoolExecutor。

《java.util.concurrent 包源码阅读》10 线程池系列之AbstractExecutorService的更多相关文章

  1. 《java.util.concurrent 包源码阅读》 结束语

    <java.util.concurrent 包源码阅读>系列文章已经全部写完了.开始的几篇文章是根据自己的读书笔记整理出来的(当时只阅读了部分的源代码),后面的大部分都是一边读源代码,一边 ...

  2. 《java.util.concurrent 包源码阅读》13 线程池系列之ThreadPoolExecutor 第三部分

    这一部分来说说线程池如何进行状态控制,即线程池的开启和关闭. 先来说说线程池的开启,这部分来看ThreadPoolExecutor构造方法: public ThreadPoolExecutor(int ...

  3. 《java.util.concurrent 包源码阅读》04 ConcurrentMap

    Java集合框架中的Map类型的数据结构是非线程安全,在多线程环境中使用时需要手动进行线程同步.因此在java.util.concurrent包中提供了一个线程安全版本的Map类型数据结构:Concu ...

  4. 《java.util.concurrent 包源码阅读》02 关于java.util.concurrent.atomic包

    Aomic数据类型有四种类型:AomicBoolean, AomicInteger, AomicLong, 和AomicReferrence(针对Object的)以及它们的数组类型, 还有一个特殊的A ...

  5. 《java.util.concurrent 包源码阅读》17 信号量 Semaphore

    学过操作系统的朋友都知道信号量,在java.util.concurrent包中也有一个关于信号量的实现:Semaphore. 从代码实现的角度来说,信号量与锁很类似,可以看成是一个有限的共享锁,即只能 ...

  6. 《java.util.concurrent 包源码阅读》06 ArrayBlockingQueue

    对于BlockingQueue的具体实现,主要关注的有两点:线程安全的实现和阻塞操作的实现.所以分析ArrayBlockingQueue也是基于这两点. 对于线程安全来说,所有的添加元素的方法和拿走元 ...

  7. 《java.util.concurrent 包源码阅读》09 线程池系列之介绍篇

    concurrent包中Executor接口的主要类的关系图如下: Executor接口非常单一,就是执行一个Runnable的命令. public interface Executor { void ...

  8. 《java.util.concurrent 包源码阅读》22 Fork/Join框架的初体验

    JDK7引入了Fork/Join框架,所谓Fork/Join框架,个人解释:Fork分解任务成独立的子任务,用多线程去执行这些子任务,Join合并子任务的结果.这样就能使用多线程的方式来执行一个任务. ...

  9. 《java.util.concurrent 包源码阅读》24 Fork/Join框架之Work-Stealing

    仔细看了Doug Lea的那篇文章:A Java Fork/Join Framework 中关于Work-Stealing的部分,下面列出该算法的要点(基本是原文的翻译): 1. 每个Worker线程 ...

随机推荐

  1. sublime Text 正则替换

    我遇到一个文章,需要把所有的 (数字) 换为 [数字] 于是我使用 Sublime Text的替换 首先,我们需要打开正则使用"Alt+R" 或打开"Ctrl+h&quo ...

  2. sap表维护工具来维护自定义表&视图簇的使用

    一.通过表维护工具维护自定义表 1.SE11创建表 2.se11界面的菜单:实用程序->Table Maintenance Generator其实这里就是调用SE54 3.sm30 调用维护好的 ...

  3. 63、django之模版层(template)

    上篇主要介绍了django的MTV模型,主要介绍了视图层之路由配置系统url分发和视图层之视图函数view,本篇主要讲解MTV模型中的模版层template. 模版层(template) 一.模版简介 ...

  4. 为Android设备添加A2SD支持

          相信很多用Android设备的用户都有这个问题,内部存储太小导致应用只能装那么几个,虽然rom也有提供移动到sd卡的选项,但是仅仅是移动程序文件到sd卡,并不能解决多少问题,多装几个还是会 ...

  5. Ceph: A Scalable, High-Performance Distributed File System译文

    原文地址:陈晓csdn博客 http://blog.csdn.net/juvxiao/article/details/39495037 论文概况 论文名称:Ceph: A Scalable, High ...

  6. codeblocks无法编译的问题

    (题外话:网上垃圾资源太多,良心推荐下载 codeblocks的码农们,别TM用DevC++,百度搜索100个不用devc++的理由加上我自己亲身经历!!!) https://jingyan.baid ...

  7. Spring MVC前后端的数据传输

    本篇文章主要介绍了Spring MVC中如何在前后端传输数据. 后端 ➡ 前端 在Spring MVC中这主要通过Model将数据从后端传送到前端,一般的写法为: @RequestMapping(va ...

  8. Python学习笔记_02:使用Tkinter连接MySQL数据库实现登陆注册功能

    1 环境搭建 1.1 Python安装 1.2 MySQL环境搭建 1.3安装MySQLdb  2 具体实现 2.1 登陆界面 2.2 注册界面 2.3 具体实现部分代码   1 环境搭建 1.1 P ...

  9. c# Invoke和Begininvoke区别

    一.对Invoke和Begininvoke的认识 1.Invoke():同步委托,会阻塞当前主线程的运行,等待invoke()方法返回才执行后面的代码: 2.Begininvoke():异步委托,调用 ...

  10. Cable master

    Problem Description Inhabitants of the Wonderland have decided to hold a regional programming contes ...