先看一段FutureTask获取线程返回值简单应用的代码,以下基于jdk8进行源码分析。
package com.lanhuigu.demo.createthread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 实现Callable接口,获取线程执行返回值
* @author yihonglei
* @date 2018/9/12 16:43
*/
public class MyCallable implements Callable {
/**
* 实现Callable中的call方法
* @author yihonglei
* @date 2018/9/12 17:01
*/
public String call() throws Exception {
return "Test Callable";
}
public static void main(String[] args) {
/** 根据MyCallable创建FutureTask对象 */
FutureTask futureTask = new FutureTask<>(new MyCallable());
try {
/** 启动线程 */
new Thread(futureTask).start();
/** 获取线程执行返回值 */
String s = futureTask.get();
/** 打印返回值 */
System.out.println(s);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
程序运行结果:
成功拿到了线程执行的返回值。
以下从源码角度分析拿到返回值的全过程,首先需要简单了解下Callable和FutureTask的结构。
Callable是一个函数式接口,源码如下:
package java.util.concurrent;
@FunctionalInterface
public interface Callable {
V call() throws Exception;
}
该接口有一个call方法,返回任意类型值。
FutureTask实现RunnableFuture接口,而RunnableFuture继承了Runnable, Future
package java.util.concurrent;
import java.util.concurrent.locks.LockSupport;
public class FutureTask implements RunnableFuture {
......
}
package java.util.concurrent;
public interface RunnableFuture extends Runnable, Future {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
所以FutureTask具有Runnable和Future功能,因此,在上面的Demo中,以下代码具有Runable特性:
/** 根据MyCallable创建FutureTask对象 */
FutureTask futureTask = new FutureTask<>(new MyCallable());
创建线程对象,通过start()方法启动线程:
/** 启动线程 */
new Thread(futureTask).start();
start()方法源码如下:
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
// 本地方法
private native void start0();
start()方法最后会调用本地方法,由JVM通知操作系统,创建线程,最后线程通过JVM访问到Runnable中的run()方法。
而FutureTask实现了Runnable的run()方法,看下FutureTask中的run()方法源码:
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
/**
这里的callable就是我们创建FutureTask的时候传进来的MyCallable对象,
该对象实现了Callable接口的call()方法。
*/
Callable c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
/**
调用Callable的call方法,即调用实现类MyCallable的call()方法,
执行完会拿到MyCallable的call()方法的返回值“Test Callable”。
*/
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
/** 将返回值传入到set方法中,这里是能获取线程执行返回值的关键 */
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);
}
}
从run()方法源码可以知道,MyCallabel执行call()方法的返回值被传入到了一个set()方法中,能拿到线程返回值最关键的
就是这个FutureTask的set()方法源码:
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
/**
将MyCallable执行call()方法的返回值传进来赋值给了outcome,
这个outcome是FutureTask的一个成员变量。
该变量用于存储线程执行返回值或异常堆栈,通过对应的get()方法获取值。
private Object outcome;
*/
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
到这里,得出一个结论就是,MyCallable执行的call()方法结果通过FutureTask的set()方法存到了成员变量outcome中,
通过我们熟悉的get方法就可以获取到outcome对应赋的值。
在Demo中获取返回值的代码:
/** 获取线程执行返回值 */
String s = futureTask.get();
FutureTask中get()方法的源码:
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
/** 调用report方法 */
return report(s);
}
private V report(int s) throws ExecutionException {
/** outcome赋值给Object x */
Object x = outcome;
if (s == NORMAL)
/** 返回outcome的值,也就是线程执行run()方法时通过set()方法放进去的MyCallable的call()执行的返回值 */
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
get()方法调用report()方法,report()方法会将outcome赋值并返回,set方法成功拿到返回的outcome,
也就是MyCallable()的call()方法执行结果。
到这里,我们大概理解了FutureTask.get()能拿到线程执行返回值的本质原理,也就基于FutureTask的成员变量
outcome进行的set赋值和get取值的过程。
下面简单总结一下过程:
1)FutureTask通过MyCallable创建;
2)new Thread()创建线程,通过start()方法启动线程;
3)执行FutureTask中的run()方法;
4)run()方法中调用了MyCallable中的call()方法,拿到返回值set到FutureTask的outcome成员变量中;
5)FutureTask通过get方法获取outcome对象值,从而成功拿到线程执行的返回值;
其实,本质上就是一个基于FutureTask成员变量outcome进行的set和get的过程,饶了一圈而已。