FutureTask类图
君欲善其事,必先利其器,咱们要想搞清楚RxJava的线程调度器,这章是基础,先补充下基础知识。
我们首先来画一下类图,好分析这个类的逻辑流程以及功能:
从图中可以知道,它继承了runnable接口以及future接口,那么说明这个类可以用来执行线程的异步任务,也可以用来将异步结果同步接受。
public static void main() {
ExecutorService executor = Executors.newCachedThreadPool();
Task task = new Task();
Future<Integer> result = executor.submit(task);
executor.shutdown();
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("主线程在执行任务");
try {
System.out.println("task运行结果"+result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("所有任务执行完毕");
}
class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("子线程在进行计算");
Thread.sleep(3000);
int sum = 0;
for(int i=0;i<100;i++)
sum += i;
return sum;
}
}
运行打印的结果如下:
I/System.out: 子线程在进行计算
I/System.out: 主线程在执行任务
I/System.out: task运行结果4950
I/System.out: 所有任务执行完毕
我们知道FutureTask这个类的作用就是可以让耗时操作的结果可以以同步的形式返回。上面的代码就是建立一个线程池,然后将我们的Task传入线程池,然后返回了一个Future result 对象。这个返回的是FutureTask的接口,实际上还是futureTask对象。
后面就可以直接调用result.get()获取返回结果。
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
}
//适配器模式将参数包装成callable对象
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
run方法
既然我们FutureTask是个runnable接口,所以一定是从run()方法开始分析了,代码如下:
public void run() {
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
..省略
}
if (ran)
set(result);
}
} finally {
...省略
}
}
就是在线程中调用我们的callable.call()方法,将result返回,使用的是set(result)方法去设置返回值。我们来看下set方法是怎么做的:
protected void set(V v) {
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
outcome = v;
U.putOrderedInt(this, STATE, NORMAL); // final state
finishCompletion();
}
}
首先就是if判断,它的意思就是将state从NEW设置成COMPLETING状态(CAS乐观锁操作,有兴趣自己了解下),接下来如果if成立的话,将结果设置给outcome变量,接着将状态设置成NORMAL(一样的CAS操作)。并且调用finishCompletion()方法。
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (U.compareAndSwapObject(this, WAITERS, 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
}
上面的waiters是所有调用了get()方法之后处于阻塞状态的线程链表,这里是将该链表上所有的节点都摘下里,然后置空,帮助内存回收。run方法的分析基本上就告一段落了。
get方法
futureTask的目的就是为了能够同步的将耗时操作的结果返回,所以我们从调用get方法的来看是怎么实现的:
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);
}
从上面的方法可以看得到,首先是判断if判断当前的状态是否是小于等于COMPLETE状态的,是的话进入waitDone()方法。
看了waitDone方法的源码:
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
long startTime = 0L; // Special value 0L means not yet parked
WaitNode q = null;
boolean queued = false;
for (;;) {
int s = state;
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING)
Thread.yield();
else if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
else if (q == null) {
if (timed && nanos <= 0L)
return s;
q = new WaitNode();
}
else if (!queued)
queued = U.compareAndSwapObject(this, WAITERS,
q.next = waiters, q);
else if (timed) {
...省略
}
else
LockSupport.park(this);
}
}
大概意思就是for循环,一直查询状态state是否大于COMPLETING状态,是的话直接返回结果;如果是等于的,那么就yield(),让出时间片,再次循环;如果是小于的话,那么会创建waitNode将节点加入到队列中。那么这些操作都做了的话,最后还是没有得到完成的结果的话,就调用最后一个else语句 LockSupport.park(this); 布尔类型timed这个不会成立的,参数就是false,这个是用于请求超时功能的调用的。
LockSupport.park(this)这个函数可以直接把它当成是await,对应的unpark相当于notify,这是新的锁机制,性能比原来的要好。
这里也就是将当前调用get的线程切换成等待状态,一直阻塞在这里。
前面我们有一段是finishCompletion方法的调用,也就是在结果产生之后,会调用unpark函数,让所有的线程都唤醒,然后继续进行for循环获取结果值。那么futureTask这个类分析就基本上结束了,功能和原理应该是很清晰了。
最后上一下,整个过程的流程图:
再补充一下,futureTask状态转化图:
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
其中第三种转移图,是当线程正在执行的时候,用户调用了cancle方法,让线程从NEW状态转化成CANCELLED状态或者INTERRUPTING状态,最终会转成INTERRUPTED状态。此时运行该Task的线程会被中断。
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW &&
U.compareAndSwapInt(this, STATE, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
U.putOrderedInt(this, STATE, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}