目录
一.简介
一般使用Thread时,都是传入Runnable,但是这种方式有一个问题:无法获取返回值,也不能抛出异常;只能通过全局变量等手段获取值,通过try-catch块捕获异常。Callable就是为了解决这个问题而产生的,它的call方法既可以有返回值,也可以抛出异常。Future则表示一个未来的值,这个值虽然当前还不存在,但是当它产生时,就可以立即获取到。
下面是一个例子:
import java.util.concurrent.*;
public class CallableTest {
public static void main(String args[]) throws ExecutionException, InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(2,5,5,
TimeUnit.SECONDS,new LinkedBlockingDeque<>());
Future<String> future = executor.submit(() -> {
Thread.sleep(5000);
return "Hello World";
});
System.out.println("现在还没获取到结果,时间戳:"+System.currentTimeMillis());
System.out.println(future.get());
System.out.println("现在已经获取到结果了,时间戳:"+System.currentTimeMillis());
}
}
输出如下:
现在还没获取到结果,时间戳:1550912985299
Hello World
现在已经获取到结果了,时间戳:1550912990300
ThreadPoolExecutor相关内容,请见:《码出高效》学习:ThreadPoolExecutor
二.Future的一些方法
从源码中可以看到,ThreadPoolExecutor实际通过newTaskFor方法构建了Future的实例,该实例实际是FutureTask对象。接下来介绍的相关方法全部是FutureTask的实现。
1)cancel方法
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, 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
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}
首先进行状态的判断,NEW表示任务还没完成,此时还可以中断。如果state不为NEW,说明任务要么已经完成,要么已经中断/取消,则没有必要取消,直接返回false。取消任务的方法是向线程发送中断信号,然后在finishCompletion中激活该线程以处理中断信号,并将它从等待队列中移除。
finishCompletion方法最后调用了done方法,不过该方法在FutureTask中是空方法,是留给开发者扩展使用的。
2)get方法
get方法很简单,就是进行状态判断,如果任务已完成,则取出数据,否则等待:
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
awaitDone使用自旋的方式等待数据,并且是可中断的:
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);
}
相比于之前看到的自旋CAS,这里有一个改进,就是如果正在处理任务(s==COMPLETING),就主动让出CPU,避免自旋消耗太多CPU资源。
3)run方法
FutureTask实现的RunnableFuture接口,继承了Future和Runnable两个接口,这也是它可以被ExecutorService的execute方法接受的原因。线程池正是调用该方法来处理任务的。
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 = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
代码非常简单,就是:状态检查 —— 调用call方法 —— 收尾&异常处理 三部分。