JDK future框架,提供了一种异步编程模式,基于线程池的。将任务runnable/callable提交到线程池executor,返回一个Future对象。通过future.get()获取执行结果,这里提交到线程池,后面的操作不会阻塞。future.get()获取结果会阻塞,其实也是用多线线程执行任务。

future.get()这里会阻塞,google的guava提供了一个calllback解决办法,这也是我准备看的

下面是一个future的demo

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; /**
* @Author: <guanxianseng@163.com>
* @Description: jdk future demo
* @Date: Created in : 2018/11/30 6:01 PM
**/
public class TestJdkFuture { public static void main(String[] args) throws ExecutionException, InterruptedException {
testJdkFuture();
} public static void testJdkFuture() throws ExecutionException, InterruptedException {
Callable<Integer> callable = () ->{
System.out.println("callable do some compute");
return 1;
} ;
Runnable runnable = () -> {
System.out.println("runable do some compute");
};
ExecutorService executorService = Executors.newCachedThreadPool();
Future<Integer> future = executorService.submit(callable);
Future runableFuture = executorService.submit(runnable);
Object runableRes = runableFuture.get();
int res = future.get();
executorService.shutdown();
System.out.println("callable res: " + res);
System.out.println("runnable res: " + runableRes); }
}

demo里面提交了一个Callable和一个Runnable到线程池,通过future获取计算结果

executorService.submit(callable)源码

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

这里主要是封装了一个RunnableFuture和提交任务到线程池

我们看下RunnableFuture里面的run方法,因为线程池执行任务,是执行run方法。看FutureTask里面的run方法

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

首先,设置runner为线程池中的当前线程,后面执行call()方法,计算出结果,set(result)。跟进set(result)

/**
* Sets the result of this future to the given value unless
* this future has already been set or has been cancelled.
*
* <p>This method is invoked internally by the {@link #run} method
* upon successful completion of the computation.
*
* @param v the value
*/
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}

这里用了一个cas设置FutureTask的state字段为COMPLETING,完成中的一个状态。接着设置outcom为计算结果,我们跟进finishCompletion()

/**
* Removes and signals all waiting threads, invokes done(), and
* nulls out callable.
*/
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 gc
q = next;
}
break;
}
} done(); callable = null; // to reduce footprint
}

WaitNode主要是一个阻塞线程链表,即调用future.get()方法的线程链表。这里主要作用是注意唤醒这些线程,通过LockSupport.unpark(t)唤醒。这里用阻塞线程链表,主要是考虑到可能有多个线程会调用future.get()阻塞

ok,到这里执行任务,把计算结果放到future中,并唤醒阻塞线程已经理清楚了

我们再来看下,future.get()是如何实现阻塞,和获取到计算结果的

进入future.get()

/**
* Waits if necessary for the computation to complete, and then
* retrieves its result.
*
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an
* exception
* @throws InterruptedException if the current thread was interrupted
* while waiting
*/
V get() throws InterruptedException, ExecutionException;

Future.java是一个接口,这里看注释可以看出,会阻塞等待结果计算完成。我们看下一个实现FutureTask.java的get()方法

/**
* @throws CancellationException {@inheritDoc}
*/
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}

awaitDone(false, 0l)主要是阻塞线程,进入方法

/**
* Awaits completion or aborts on interrupt or timeout.
*
* @param timed true if use timed waits
* @param nanos time to wait, if timed
* @return state upon completion
*/
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 yet
Thread.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);
}
else
LockSupport.park(this);
}
}

这里,主要是阻塞线程,把当前线程放到阻塞线程链表中,通过LockSupport.park(this)阻塞当前线程,等待线程池里面的线程唤醒。唤醒之后,回到get()方法,看report()方法

/**
* Returns result or throws exception for completed task.
*
* @param s completed state value
*/
@SuppressWarnings("unchecked")
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}

这里主要是返回计算结果给阻塞线程

到这里,基本理清楚了,future的阻塞实现,以及获取计算结果的步骤。

future框架 = 线程池 + futrue

Java Future源码分析的更多相关文章

  1. Java Reference 源码分析

    @(Java)[Reference] Java Reference 源码分析 Reference对象封装了其它对象的引用,可以和普通的对象一样操作,在一定的限制条件下,支持和垃圾收集器的交互.即可以使 ...

  2. Java 集合源码分析(一)HashMap

    目录 Java 集合源码分析(一)HashMap 1. 概要 2. JDK 7 的 HashMap 3. JDK 1.8 的 HashMap 4. Hashtable 5. JDK 1.7 的 Con ...

  3. java集合源码分析(三):ArrayList

    概述 在前文:java集合源码分析(二):List与AbstractList 和 java集合源码分析(一):Collection 与 AbstractCollection 中,我们大致了解了从 Co ...

  4. java集合源码分析(六):HashMap

    概述 HashMap 是 Map 接口下一个线程不安全的,基于哈希表的实现类.由于他解决哈希冲突的方式是分离链表法,也就是拉链法,因此他的数据结构是数组+链表,在 JDK8 以后,当哈希冲突严重时,H ...

  5. Java集合源码分析(六)TreeSet<E>

    TreeSet简介 TreeSet 是一个有序的集合,它的作用是提供有序的Set集合.它继承于AbstractSet抽象类,实现了NavigableSet<E>, Cloneable, j ...

  6. Java集合源码分析(五)HashSet<E>

    HashSet简介 HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持.它不保证set 的迭代顺序:特别是它不保证该顺序恒久不变.此类允许使用null元素. HashSet源 ...

  7. Java集合源码分析(四)Vector<E>

    Vector<E>简介 Vector也是基于数组实现的,是一个动态数组,其容量能自动增长. Vector是JDK1.0引入了,它的很多实现方法都加入了同步语句,因此是线程安全的(其实也只是 ...

  8. Java集合源码分析(三)LinkedList

    LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈.队列和双端队列来使用. LinkedList同样是非线程安全 ...

  9. Java集合源码分析(二)ArrayList

    ArrayList简介 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存. ArrayList不是线程安全的,只能用在单线程环境下,多线 ...

随机推荐

  1. Jzoj 初中2249 蒸发学水(并查集)

    题目描述 众所周知,TerryHu 是一位大佬,他平时最喜欢做的事就是蒸发学水. 机房的位置一共有n 行m 列,一开始每个位置都有一滴学水,TerryHu 决定在每一个时刻选择 一滴学水进行蒸发,直到 ...

  2. Python 用队列实现多线程并发

    # Python queue队列,实现并发,在网站多线程推荐最后也一个例子,比这货简单,但是不够规范 # encoding: utf-8 __author__ = 'yeayee.com' # 由本站 ...

  3. 查看npm全局安装位置

    查看npm全局安装位置:npm config get prefix 设置位置:npm config set prefix 填写位置

  4. mxonline实战11,课程详情页2,课程章节页

    对应github地址:第11天   一. 课程详情页2   1. 课程详情页第2块中的课程介绍中,修改course-detail.html中代码,搜索课程详情,找到如下代码

  5. Commands that may modify the data set are disabled, because this instance is configured to report errors during writes if RDB snapshotting fails (stop-writes-on-bgsave-error option)

    今天运行Redis时发生错误,错误信息如下: org.springframework.dao.InvalidDataAccessApiUsageException: MISCONF Redis is ...

  6. JDK8的安装及环境配置

    原文链接:https://www.cnblogs.com/chenxj/p/10137221.html 1.下载JDK: a.直接官网下载:http://www.oracle.com/: b.或百度网 ...

  7. Python的垃圾回收机制以及引用计数

    Python中的计数引用 在Python中,由于Python一门动态的语言,内部采用的指针形式对数据进行标记的,并不像c/c++那样,通过指定的数据类型并分配相应的数据空间,Python中定义的变量名 ...

  8. es6学习 1

    块级作用域绑定 一 var 声明及变量提升(Hoisting)机制 在函数作用域或全局作用域中通过 var 声明的变量,无论实际上是在哪里声明的,都会被当成在当前作用域顶部声明的变量,这就是我们常说的 ...

  9. Jexus进程守护工具jws.guard

    一个运行中的进程,难免会因为各种各样的原因无缘无故的宕掉(比如网站瞬间的负载过高.内存不足等),而Jexus宕掉的后果往往只有一个:对外提供服务的网站无法访问了.因此,我们需要最大限度的保障我们的网站 ...

  10. BigDecimal加减乘除运算(转)

    java.math.BigDecimal.BigDecimal一共有4个够造方法,让我先来看看其中的两种用法: 第一种:BigDecimal(double val) Translates a doub ...