1 系列目录

  • 线程池接口分析以及FutureTask设计实现
  • 线程池源码分析-ThreadPoolExecutor

该系列打算从一个最简单的Executor执行器开始一步一步扩展到ThreadPoolExecutor,希望能粗略的描述出线程池的各个实现细节。针对JDK1.7中的线程池

2 Executor接口说明

Executor执行器,就是执行一个Runnable任务,可同步可异步,接口定义如下:

public interface Executor {/*** Executes the given command at some time in the future.  The command* may execute in a new thread, in a pooled thread, or in the calling* thread, at the discretion of the <tt>Executor</tt> implementation.** @param command the runnable task* @throws RejectedExecutionException if this task cannot be* accepted for execution.* @throws NullPointerException if command is null*/void execute(Runnable command);
}

ExecutorService则继承了Executor,描述了线程池应该具有的功能特性,来详细看下接口,这些接口都有详细的文档可以阅读,这里就不再列出来了,目前只说明我们重点关注的接口。

<T> Future<T> submit(Callable<T> task);

可以提交一个Callable,并且返回一个Future用于追踪提交的任务。如何追踪一个任务的状态和返回数据呢?那就需要将提交的任务进行封装,对任务的执行、执行过程中的异常、中断、返回结果进行统一的监控处理。下面就来看看AbstractExecutorService对上述submit的实现

public <T> Future<T> submit(Callable<T> task) {if (task == null) throw new NullPointerException();RunnableFuture<T> ftask = newTaskFor(task);execute(ftask);return ftask;
}protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {return new FutureTask<T>(callable);
}

从上面看到就是对Callable封装成一个新的任务,即FutureTask,调用Executor的原始接口execute方法来执行FutureTask,并且返回给用户FutureTask对象,用于追踪任务的状态和数据,下面就需要我们来详细看看FutureTask如何对任务进行封装的

3 FutureTask的实现细节

3.1 FutureTask的属性和构造函数

private volatile int state;
private static final int NEW          = 0;
private static final int COMPLETING   = 1;
private static final int NORMAL       = 2;
private static final int EXCEPTIONAL  = 3;
private static final int CANCELLED    = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED  = 6;/** The underlying callable; nulled out after running */
private Callable<V> callable;
/** The result to return or exception to throw from get() */
private Object outcome; // non-volatile, protected by state reads/writes
/** The thread running the callable; CASed during run() */
private volatile Thread runner;
/** Treiber stack of waiting threads */
private volatile WaitNode waiters;public FutureTask(Callable<V> callable) {if (callable == null)throw new NullPointerException();this.callable = callable;this.state = NEW;       // ensure visibility of callable
}

有一个状态变量state,一个Callable callable即原始任务,Object outcome存放原始任务的输出结果或者异常,Thread runner运行该任务的线程,WaitNode waiters等待获取任务结果的等待者

3.2 FutureTask的get方法实现

使用FutureTask阻塞式等待任务执行结果,一种是永远阻塞另一种就是阻塞一定时间否则报超时异常,如下2个方法

public V get() throws InterruptedException, ExecutionException {int s = state;if (s <= COMPLETING)s = awaitDone(false, 0L);return report(s);
}public V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException {if (unit == null)throw new NullPointerException();int s = state;if (s <= COMPLETING &&(s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)throw new TimeoutException();return report(s);
}

阻塞式等待的核心逻辑就在上述awaitDone方法中,来详细看看

private int awaitDone(boolean timed, long nanos)throws InterruptedException {final long deadline = timed ? System.nanoTime() + nanos : 0L;WaitNode q = null;boolean queued = false;for (;;) {if (Thread.interrupted()) {removeWaiter(q);throw new InterruptedException();}int s = state;if (s > COMPLETING) {if (q != null)q.thread = null;return s;}else if (s == COMPLETING) // cannot time out yetThread.yield();else if (q == null)q = new WaitNode();else if (!queued)queued = UNSAFE.compareAndSwapObject(this, waitersOffset,q.next = waiters, q);else if (timed) {nanos = deadline - System.nanoTime();if (nanos <= 0L) {removeWaiter(q);return state;}LockSupport.parkNanos(this, nanos);}elseLockSupport.park(this);}
}

可以看到有一个for循环不断处理着各种情况:

1 从最开始的WaitNode q = null,构建了一个WaitNode,即代表着当前线程作为一个等待者,WaitNode就是一个简单的链表,如下

static final class WaitNode {volatile Thread thread;volatile WaitNode next;WaitNode() { thread = Thread.currentThread(); }
}

2 构建好WaitNode之后就要将该WaitNode放入链表中,这时候就会涉及多线程问题,使用UNSAFE的CAS来解决,这种方式也是AtomicLong等众多原子类的底层实现方式

3 成功放入WaitNode链表之后,采用LockSupport的park阻塞当前线程,要么只阻塞一定时间要么一直阻塞,直到被LockSupport的unpark唤醒。LockSupport在锁的底层实现AQS中也非常常见,使用了LockSupport就可以不用在for循环里不断判断当前任务状态而浪费CPU,只需要当前任务完成之后,使用LockSupport对等待线程进行unpark,就可以使等待的线程退出等待继续往下执行

4 如果LockSupport阻塞时间到了,还未收到unpark,则需要从等待者链表中删除当前线程代表的等待者

3.3 FutureTask的任务执行过程

public void run() {if (state != NEW ||!UNSAFE.compareAndSwapObject(this, runnerOffset,null, Thread.currentThread()))return;try {Callable<V> c = callable;if (c != null && state == NEW) {V result;boolean ran;try {result = c.call();ran = true;} catch (Throwable ex) {result = null;ran = false;setException(ex);}if (ran)set(result);}} finally {// runner must be non-null until state is settled to// prevent concurrent calls to run()runner = null;// state must be re-read after nulling runner to prevent// leaked interruptsint s = state;if (s >= INTERRUPTING)handlePossibleCancellationInterrupt(s);}
}

1 一旦FutureTask任务开始执行了,就需要将当前执行线程设置到FutureTask的volatile Thread runner属性中

2 执行原始任务Callable的call方法,可能成功也可能失败也可能被中断被取消

文档中有如下状态的迁移过程:

Possible state transitions:* NEW -> COMPLETING -> NORMAL* NEW -> COMPLETING -> EXCEPTIONAL* NEW -> CANCELLED* NEW -> INTERRUPTING -> INTERRUPTED

来看下成功和失败方法

protected void set(V v) {if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {outcome = v;UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final statefinishCompletion();}
}protected void setException(Throwable t) {if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {outcome = t;UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final statefinishCompletion();}
}

都是首先将状态变成COMPLETING正在结束中,然后设置outcome,成功则设置正常的返回值,失败则设置成异常,然后根据划定最终的状态结果,成功就是NORMAL,失败就是EXCEPTIONAL,最后呢调用finishCompletion,去unpark之前说的WaitNode中对应的线程们

private void finishCompletion() {// assert state > COMPLETING;for (WaitNode q; (q = waiters) != null;) {if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {for (;;) {Thread t = q.thread;if (t != null) {q.thread = null;LockSupport.unpark(t);}WaitNode next = q.next;if (next == null)break;q.next = null; // unlink to help gcq = next;}break;}}done();callable = null;        // to reduce footprint
}

这里就是遍历WaitNode链表,对每一个WaitNode对应的线程依次进行LockSupport.unpark(t),使其结束阻塞。WaitNode通知完毕后,调用一个done方法,目前该方法是空的实现,所以你如果想在任务完成后执行一些动作的时候就可以重写该方法

有一个问题就是:为什么一定要加入COMPLETING状态呢?能不能直接过度到NORMAL或者EXCEPTIONAL?

目前我的理解是:NORMAL或者EXCEPTIONAL是一种最终状态,所以在出现该状态前,outcome必须已经被设置了,即有如下代码:

protected void set(V v) {outcome = v;UNSAFE.compareAndSwapInt(this, stateOffset, NEW, NORMAL)finishCompletion();
}

但是因为存在外部直接取消该任务,所以结果状态的设置和outcome必须是同步的,且outcome在前,为了保证代码的同步可以使用锁

protected void set(V v) {synchronized(){outcome = v;UNSAFE.compareAndSwapInt(this, stateOffset, NEW, NORMAL)finishCompletion();}
}

为了减少锁带来的开支,就可以引入一个中间状态COMPLETING,通过CAS来间接实现锁的竞争,同时又保证outcome在最终状态NORMAL或者EXCEPTIONAL之前被设置

3.4 FutureTask任务的取消

public boolean cancel(boolean mayInterruptIfRunning) {if (state != NEW)return false;if (mayInterruptIfRunning) {if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, INTERRUPTING))return false;Thread t = runner;if (t != null)t.interrupt();UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); // final state}else if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, CANCELLED))return false;finishCompletion();return true;
}

取消任务,有2种情况,一种该任务正在运行,一种就是非运行状态,所以需要用户给出明示是否中断正在运行的任务,即需要一个参数mayInterruptIfRunning

中断任务就是通过中断运行该任务的线程,即直接调用该线程的interrupt()方法

4 结束语

FutureTask大部分就简单分析完了,其他的自己去看下就行了。至此我们了解了一个任务被提交经过了封装,变成了一个新的任务FutureTask,同时FutureTask也明确了该任务的整个执行过程,只留出核心execute(futureTask)方法需要被子类来实现,下一篇文章就重点介绍下ThreadPoolExecutor对该核心方法的实现

线程池源码分析-FutureTask相关推荐

  1. 吐血整理:Java线程池源码分析(基于JDK1.8建议收藏)

    文章目录 一.引言 二.线程池的参数介绍 1.ThreadPoolExecutor的UML图 三.线程池的使用 1.线程池的工作原理 2.线程池类型 2.1.newCachedThreadPool使用 ...

  2. Java线程池 源码分析

    1.个人总结及想法: (1)ThreadPoolExecutor的继承关系? ThreadPoolExecutor继承AbstractExectorService,AbstractExecutorSe ...

  3. java 线程池 源码_java线程池源码分析

    我们在关闭线程池的时候会使用shutdown()和shutdownNow(),那么问题来了: 这两个方法又什么区别呢? 他们背后的原理是什么呢? 线程池中线程超过了coresize后会怎么操作呢? 为 ...

  4. 线程池源码分析之ThreadPoolExecutor

    前言 今天老吕给大家来分享下ThreadPoolExecutor 线程池的实现逻辑,大家伙认真看,一般人我不告诉他的. 线程池相关类图 JDK中线程池相关的类结构关系图 获取不同特性的线程池 在Exe ...

  5. 线程池之ScheduledThreadPoolExecutor线程池源码分析笔记

    1.ScheduledThreadPoolExecutor 整体结构剖析. 1.1类图介绍 根据上面类图图可以看到Executor其实是一个工具类,里面提供了好多静态方法,根据用户选择返回不同的线程池 ...

  6. Zygote pre-fork线程池源码分析

    前言 在Android Q上,google为了加快应用的启动速度.在zygote fork阶段,采用了线程池的方式,来加快fork的过程. 首先,如果让我们自己做,肯定会选择java的线程池模型,先创 ...

  7. java线程池_Java 并发编程 线程池源码实战

    作者 | 马启航 杏仁后端工程师.「我头发还多,你们呢?」 一.概述 笔者在网上看了好多的关于线程池原理.源码分析相关的文章,但是说实话,没有一篇让我觉得读完之后豁然开朗,完完全全的明白线程池,要么写 ...

  8. Java 并发编程 -- 线程池源码实战

    一.概述 小编在网上看了好多的关于线程池原理.源码分析相关的文章,但是说实话,没有一篇让我觉得读完之后豁然开朗,完完全全的明白线程池,要么写的太简单,只写了一点皮毛,要么就是是晦涩难懂,看完之后几乎都 ...

  9. java futuretask 源码解析_Java异步编程——深入源码分析FutureTask

    Java的异步编程是一项非常常用的多线程技术. 之前通过源码详细分析了ThreadPoolExecutor<你真的懂ThreadPoolExecutor线程池技术吗?看了源码你会有全新的认识&g ...

最新文章

  1. 8月第3周回顾:四巨头发三大新闻 一报告引多家争议
  2. 习题8-5 使用函数实现字符串部分复制 (20 分)
  3. .Net QQ互联教程
  4. Web 2.0技术对SEO的影响
  5. 【javascript】javascript设计模式mixin模式
  6. (1110, “Column ‘arriveTime‘ specified twice“)
  7. APP模拟手势高级操作
  8. Windows系统下nodejs安装及配置
  9. java下载图片到手机相册_Unity保存图片到Android手机且更新相册
  10. 绕过模拟器检测_和平精英:光子重点打击外设与模拟器,违规将封禁365天
  11. ubuntu 12.04 mysql_Ubuntu 12.04 mysql 源码安装--mysql.5.5.x
  12. Using LogMiner
  13. Android 学习笔记【基础扫盲篇】
  14. JMeter接口测试中,响应数据中文显示乱码的处理方法(转)
  15. Appium系列教程
  16. SQL日期时间格式转换大全
  17. pacman+s+java_ArchLinux pacman安装openjdk
  18. Android 状态栏常规操作(状态栏显示,状态栏颜色,沉浸式状态栏)
  19. java如何控制远程桌面_Java实现屏幕抓图,控制远程桌面
  20. 网络基础之DNS、网关

热门文章

  1. 三维CNN:收集一些最近的3d卷积网络PointNet++
  2. ObjecT4:On-line multiple instance learning (MIL)学习
  3. VTK+MFC 系列教程 非常强大
  4. MicroPython 1.8.6重新支持512K的模块
  5. Ember版本小小结
  6. OneAPM Cloud Test——系统性能监控神器
  7. Qucs 产生大文件的一个bug
  8. Scott Mitchell 的ASP.NET 2.0数据教程之三十九:: 在编辑和插入界面里添加验证控件...
  9. org.apache.hadoop.hive.metastore.api.SerDeInfo; local class incompatible
  10. flink的DAG可视化使用(visualizer的使用)