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. GraphQL 如何取代 Redux

    简评:使用 GraphQL 可以大大简化客户端状态管理部分的代码. ⚛️切换到React 故事背景:在 2016 年,Pathwright 的前端团队就开始将客户端的代码从 Backbone & ...

  2. Spring 并发事务的探究

    前言 在目前的软件架构中,不仅存在单独的数据库操作(一条SQL以内,还存在逻辑性的一组操作.而互联网软件系统最少不了的就是对共享资源的操作.比如热闹的集市,抢购的人群对同见商品的抢购由一位售货员来处理 ...

  3. Working with Metal—Overview

    看完这个 WWDC 之后的总结. Metal 可以在单位时间内提供 10 倍的 draw call 调用. Background About Draw Call 每一次 draw call 调用都必须 ...

  4. iOS 之新特性界面

    1.什么事新特性界面? 新特性界面就是第一次下载程序出现的界面,他的用途是帮助用户快速了解这款APP,所有说还是很有必要学一下的. 2.如何实现新特性界面? 实现思路:从本质上看,新特性界面就是一个全 ...

  5. leetcode-217-Contains Duplicate(使用排序来判断整个数组有没有重复元素)

    题目描述: Given an array of integers, find if the array contains any duplicates. Your function should re ...

  6. ORACLE 动态执行SQL语句

    本文转自 http://zhaisx.iteye.com/blog/856472 Oracle 动态SQL Oracle 动态SQL有两种写法:用 DBMS_SQL 或 execute immedia ...

  7. FlowPortal-BPM——离线审批(邮箱审批)配置

    一.将系统文件复制到安装目录下 二.以管理员身份运行bat安装程序 三.开启邮件离线审批服务 四.创建数据库表(JAVA数据库 或 .Net数据库) 五.配置config文件(发件箱.收件箱.错误问题 ...

  8. Python爬虫之XML

    一.请求参数形式为xml 举例说明. 现在有这样一个网址:https://www.runff.com/html/live/s1484.html:想要查询图片列表,打开F12,观察到请求如下: 这里的请 ...

  9. php工具箱使用

    linux安装过程 1,去网盘下载(本人网盘也有,如果找不到,contact me)http://pan.baidu.com/s/1cxHQge 我解压以后放在/mnt/hgfs/root/phpto ...

  10. erlang尾递归练习

    1 %%计算链表长度尾递归实现 2 lez(N) -> lez(N, 0). 3 4 lez([], Acc) -> Acc; 5 lez([_ | T], Acc) -> lez( ...