1 基本概念

1.1 Callable与Future

Runnable封装一个异步运行的任务,可以把它想象成为一个没有参数和返回值的异步方法。Callable与Runnable类似,但是有返回值。Callable接口是一个参数化的类型,只有一个方法call。

public interface Callable<V> {
V call() throws Exception;
}

类型参数是返回值的类型。例如,

Callable<Integer>表示一个最终返回Integer对象的异步计算。

Future保存异步计算的结果。可以启动一个计算,将Future对象交给某个线程,然后忘掉它。Future对象的所有者在结果计算好之后就可以获得它。

Future接口具有下面的方法:

public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

第一个get方法的调用被阻塞,知道计算完成。如果在计算完成之前,第二个get方法的调用超时,抛出一个TimeoutException异常。如果运行该计算的线程被中断,两个方法都将抛出InterruptedException。如果计算已经完成,那么get方法立即返回。

如果计算还在进行,isDone方法返回false;如果完成了,则返回true。

可以用cancel方法取消该计算。如果计算还没有开始,它被取消且不再开始。如果计算处于运行之中,那么如果mayInterrupt参数为true,它就被中断。

1.2 FutureTask

FutureTask包装器是一种非常便利的机制,同时实现了Future和Runnable接口。

类图如下:

FutureTask的状态转换过程:

 * NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED

1.3 FutureTask的执行过程

创建一个futureTask对象task
提交task到调度器executor等待调度或者在另外一个线程中执行task 等待调度中... 如果此时currentThread调取执行结果task.get(),会有几种情况
if task 还没有被executor调度或正在执行中
阻塞当前线程,并加入到一个阻塞链表中waitNode
else if task被其它Thread取消,并取消成功 或task处于中断状态
throw exception
else if task执行完毕,返回执行结果,或执行存在异常,返回异常信息 如果此时有另外一个线程调用task.get() 执行过程同上

2 应用场景

1. Future用于异步获取执行结果或者取消任务。
2. 在高并发场景下确保任务只执行一次。

3 基本例子

Callable<Integer> myComputation = ...;
FutureTask<Integer> task = new FutureTask<Integer>(myComputation);
Thread t = new Thread(task);
t.start();
...
Integer result = task.get(); //获取结果

4 FutureTask源码分析

4.1 核心状态

 /**
* The run state of this task, initially NEW. The run state
* transitions to a terminal state only in methods set,
* setException, and cancel. During completion, state may take on
* transient values of COMPLETING (while outcome is being set) or
* INTERRUPTING (only while interrupting the runner to satisfy a
* cancel(true)). Transitions from these intermediate to final
* states use cheaper ordered/lazy writes because values are unique
* and cannot be further modified.
*
* Possible state transitions:
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
*/
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;

4.2 创建FutureTask

public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
} public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}

4.3 获取执行结果

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);
}

4.4 执行方法

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 interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}

4.5 设置状态

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

5 高级示例

public class Memoizer<A, V> implements Computable<A, V> {
private final ConcurrentMap<A, Future<V>> cache = new ConcurrentMap<A, Future>>();
private final Computable<A, V> c; public Memoizer(Computable<A, V> c) {
this.c = c;
} public C computer(final A arg) throws InterruptedException {
while(true) {
Future<V> f = cache.get(arg);
if(f == null) {
Callable<V> eval = new Callable<V>() {
public V call() throws InterruptedException {
return c.compute(arg);
}
};
FutureTask<V> ft = new FutureTask<V>(eval);
f = cache.putIfAbsent(arg, ft);
if(f == null) {
f = ft;
ft.run();
}
} try {
return f.get();
} catch(CancellationException e) {
cache.remove(arg, f);
} catch(ExecutionException e) {
throw launderThrowable(e.getCause());
}
}
}
}

FutureTask详解的更多相关文章

  1. Callable,Future和FutureTask详解

    1.Callable和Runnable 看Callable接口: public interface Callable<V> { /** * Computes a result, or th ...

  2. Future、Callable 、FutureTask详解

    1.Future和Callable Future是一个接口表示异步计算的结果,它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果.Future提供了get().cancel().isC ...

  3. [转]FutureTask详解

     FutureTask类是Future 的一个实现,并实现了Runnable,所以可通过Excutor(线程池) 来执行,也可传递给Thread对象执行.如果在主线程中需要执行比较耗时的操作时,但又不 ...

  4. Java并发编程的艺术笔记(九)——FutureTask详解

    FutureTask是一种可以取消的异步的计算任务.它的计算是通过Callable实现的,多用于耗时的计算. 一.FutureTask的三种状态 二.get()和cancel()执行示意 三.使用 一 ...

  5. 最强Java并发编程详解:知识点梳理,BAT面试题等

    本文原创更多内容可以参考: Java 全栈知识体系.如需转载请说明原处. 知识体系系统性梳理 Java 并发之基础 A. Java进阶 - Java 并发之基础:首先全局的了解并发的知识体系,同时了解 ...

  6. (转)深入详解Java线程池——Executor框架

    转:https://yq.aliyun.com/articles/633782?utm_content=m_1000015330 在Java中,使用线程来异步执行任务.Java线程的创建与销毁需要一定 ...

  7. JAVA线程池原理详解二

    Executor框架的两级调度模型 在HotSpot VM的模型中,JAVA线程被一对一映射为本地操作系统线程.JAVA线程启动时会创建一个本地操作系统线程,当JAVA线程终止时,对应的操作系统线程也 ...

  8. Java多线程编程中Future模式的详解

    Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...

  9. 详解Executor框架

    在Java中,使用线程来异步执行任务.Java线程的创建与销毁需要一定的开销,如果我们为每一个任务创建一个新线程来执行,这些线程的创建与销毁将消耗大量的计算资源.同时,为每一个任务创建一个新线程来执行 ...

随机推荐

  1. Spark SQL 编程

    Spark SQL的依赖 Spark SQL的入口:SQLContext 官方网站参考 https://spark.apache.org/docs/1.6.2/sql-programming-guid ...

  2. NVMe on RHEL7

    原文地址https://www.dell.com/support/article/cn/zh/cnbsd1/sln312382/nvme-on-rhel7?lang=en Posted on beha ...

  3. Jenkins Error cloning remote repo 'origin', slave node

    使用jenkins pull git上的代码,在job中配置好源码管理后,构建时出现如题错误提示: 网上的资料几乎都是在说SSH的配置问题,因为博主项目建立在本地的git服务器上,所以在源码管理中选择 ...

  4. django项目环境设置

    1.连接数据库,如mysql DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'django_comi ...

  5. Windows系统日常运维

    WINDOWS系统日常运维 http://www.docin.com/p-677263438.html

  6. MySQL数据库作发布系统的存储,一天五万条以上的增量,预计运维三年,怎么优化?

    优化应该不仅仅是数据库方面使用高性能的服务器多使用缓存页面服务器.数据库服务器.图片服务器.上传下载服务器分离数据库集群,表分割(水平分割和垂直分割)和表散列负载均衡重视每个代码开发细节,特别是大循环 ...

  7. 1.urlencoder和urldecoder的使用

    今天传url的时候乱码了.先说情形,url中有searchText=中文的情形,后台new String(searchText.getBytes(ISO-8859-1),"gbk" ...

  8. 子元素margin-top后,跟父元素一起下沉

    在一个<div>元素中嵌套一个子div,同时设置子div的margin-top,结果,父元素和子元素一起下沉,留出来个空白区域. 原因就是:        一个盒子如果没有上补白(padd ...

  9. Java使用poi从数据库读取数据生成Excel表格

    想要使用POI操作以xsl结尾的Excel,首先要下载poi相关的jar包,用到的jar有: poi-3.9.jar poi-ooxml-3.9.jar poi-ooxml-schemas-3.9.j ...

  10. JSTL标签库学习记录1-c

    JSTL全称为JSP Standard Tag Library,即JSP标准标签库. 导入JSTL相关的JAR包,jstl.jar standard.jar 导入jstl标签库: <%@tagl ...