昨天有幸看书的时候看了一下netty的chnnelfuture,其原理感觉应该跟java的callable实现的原理应该差不多,既然这样我们先将java的future先弄个明白,java的callable能通过future拿到返回值,虽然以前也知道有这么一回事但并没有过多的关注,这次正好看一下源码来搞明白其中的奥妙 首先看下面一段示意的代码.
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newSingleThreadExecutor(); final Future<Integer> future = executor.submit(new AddTask(1, 2)); //利用线程池获取future
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
future.get(); //异步获取值
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.setName("thread-future");
thread.start();
Integer result = future.get(); //异步获取值
}
既然是从submit开始我们就从这里开始分析
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task); //注意这里
execute(ftask); //标准的线程运行方式
return ftask; //返回该对象,也就是submit的返回值?
}
让我们追究一下ftask是怎么来的 往下看 实现如下
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
看到这里你大概明白了吧,返回的是一个FutureTask 其中futureTask 实现了runnable和callable接口,其实看了源码就会明白 本质上也是运行其run 方法哦,然后在run方法中调用call方法,并用一个成员变量保持call方法的返回值
所以接下来我们看一看futureTask的run方法
public void run() {
if (state != NEW || //一些状态的判断,我们看主体 暂时忽略
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable; //这里注意哦 ,我们传进来的callable接口
if (c != null && state == NEW) {
V result;
boolean ran; //判断call是否正常执行,也就是异常,中断啥的
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);
}
}
//好了 我们大概知道了future执行的原理,让我们来看看别的线程调用future.get()会发生什么 再次之前,我们需要先补充future中关于执行状态的抽象描述
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; //中断完成
//状态只有这四种走向,并且线程一段开启,就不能被取消,只能中断
NEW -> COMPLETING -> NORMAL
NEW -> COMPLETING -> EXCEPTIONAL
NEW -> CANCELLED
NEW -> INTERRUPTING -> INTERRUPTED
好了有了这些我们看看future.get发生了什么 ,
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING) //还没有完成
s = awaitDone(false, 0L); //进入这个函数
return report(s);
}
看看awaitDonn,有什么玄机呢,从方法命名也可以看出,等待完成
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); //利用CAS入对,失败下次自旋会继续进入这里
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
LockSupport.park(this); //阻塞线程
}
}
我们先看waitNode的源码 一个超级简单的类
static final class WaitNode {
volatile Thread thread; //当前线程的引用
volatile WaitNode next; //指向下一个,从而将等待的线程全部连起来
WaitNode() { thread = Thread.currentThread(); }
}
看看上面的awaitDone方法木有,通过一个无限for循环和CAS机制,来将所调用future.get的线程在没有完成之前全部阻塞掉,并且阻塞线程通过一个next指针互相关联,既然阻塞了所有线程,那么什么时候唤醒了?显然唤醒的时机就是FutureTask的run方法执行完的时机,往前面的futureTask的run方法中有一个set方法 让我们来看看个究竟
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion(); //这里关键,任务完成
}
}
finishCompletion()方法,没错线程就是在这里面实现唤醒了 继续看源码
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) { //获取waiters节点,也就是头节点
//确保只有一个线程能够执行唤醒操作,以确保唤醒的正确进行
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; //将q执行先一个节点
}
break;
}
}
done();
callable = null; // to reduce footprint
}
看上面我每行的注释应该已经说的很明白了,也就是将等待的节点从头到尾一个一个的唤醒,从而获取到结果
至此future的原理,已经讲完。最后不得不感叹,大神还是大神,不过只要我们坚持不懈的努力最终也会变成大神的,最后为了大家在一次深刻的理解future的原理,我们不适用线程池的submit方法而是自己写一个简易的submit方法来加深大家的印象,具体的实现方式如下,
package com.ACM;
import java.util.concurrent.*;
/**
* @author zoujianglin
* @date 2018/8/8 12:53
*/
public class OtherSolution {
private volatile Object result;
public static RunnableFuture sumbit(Callable callable) {
RunnableFuture ftask = newTaskFor(callable);
Thread thread = new Thread(ftask);
thread.start(); //开启线程 运行ftask的run方法,并返回ftask
return ftask;
}
protected static RunnableFuture newTaskFor(Callable callable) {
return new FutureTask(callable);
}
public static void main(String[] args) throws Exception {
final Future future = sumbit(new AddTask(1, 2)); //得到future
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
future.get(); //异步获取future结果
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.setName("thread-future");
thread.start();
Integer result = (Integer) future.get(); //异步获取future的结果
System.out.println(result);
// int[] gas = new int[]{1, 2};
//int[] cost = new int[]{2, 1};
}
static class AddTask implements Callable {
private int a, b;
public AddTask(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public Integer call() throws Exception {
Integer result = a + b;
return result;
}
}
}