从java5开始,java提供了Callable接口,Callable接口提供了一个call方法可以作为线程的执行体,但call方法比run方法功能更加强大。主要体现在:
FutureTask类实现了RunnableFuture接口,而RunnableFuture继承了Runnable和Future接口:
我们都知道,当Thread被start之后,会调用Thread的run方法,而Thread的run方法逻辑如下:
只有一行代码,调用了sync类的innerRun方法,sync为FutureTask的内部类,我们继续追踪此方法的实现:
调用的是sync的innerGet方法:
1.call方法可以有返回值;
2.call方法可以声明抛出异常。
因此我们完全可以提供一个Callable对象作为Thread的target,而该线程的线程执行体就是该Callable对象的call方法,问题是:Callable接口是java5新增的接口,而且它并没有继承Runnable接口,所以Callable接口不可以直接作为Thread的target,而且call方法还有一个返回值,那么我们如何获取call方法的返回值呢?java5提供了Future接口代表Callable接口里call方法的返回值,FutureTask是Future的实现类,FutureTask实现了Runnable接口,所以创建线程时,可以将FutureTask传入,另外,FutureTask创建时需要一个Callable参数,所以这样一来,Callable就和Thread挂钩了,Thread实际执行的是Callable的call方法,而其返回值由FutureTask获取,调用其get方法即可获得返回值。
示例代码:
Callable<Integer> c = new Callable<Integer>()
{
@Override
public Integer call() throws Exception
{
System.out.println("running...");
Thread.sleep(2000);
return 215;
}
};
FutureTask<Integer> task = new FutureTask<>(c);
new Thread(task).start();
try
{
System.out.println("result : "+task.get());
}
catch (InterruptedException e)
{
e.printStackTrace();
}
catch (ExecutionException e)
{
e.printStackTrace();
}
}
上面说到可以调用FutureTask方法获取call的返回值,那么如果call方法是个耗时操作,调用get方法的线程岂不是得一直等着call结束么?恩,确实是这样。
调用get方法的线程会阻塞。
下面从源码角度简单分析下Callable和Future:
Callable方法没啥好说的,
只有一个call方法,作为线程的执行体。
public interface Callable<V> {
V call() throws Exception;
}
FutureTask类实现了RunnableFuture接口,而RunnableFuture继承了Runnable和Future接口:
Runnable接口大家都很熟悉,那么这个Future接口提供了什么样的方法呢?
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);//取消Future里面关联的call方法
boolean isCancelled();//任务是否取消
boolean isDone();//任务是否完成
V get() throws InterruptedException, ExecutionException;//获取call方法的结果
V get(long timeout, TimeUnit unit)//设置超时时间,超过此时间未收到结果将抛出异常
throws InterruptedException, ExecutionException, TimeoutException;
}
我们都知道,当Thread被start之后,会调用Thread的run方法,而Thread的run方法逻辑如下:
@Override
public void run() {
if (target != null) {
target.run();
}
}
即判断是否传入了Runnable类型的target,如果有,将会执行Runnable的run方法,这里我们传入的是FutureTask,所以会调用FutureTask类的run方法:
public void run() {
sync.innerRun();
}
只有一行代码,调用了sync类的innerRun方法,sync为FutureTask的内部类,我们继续追踪此方法的实现:
void innerRun() {
if (!compareAndSetState(READY, RUNNING))
return;
runner = Thread.currentThread();
if (getState() == RUNNING) { // recheck after setting thread
V result;
try {
result = callable.call();//调用的是callable的call方法
} catch (Throwable ex) {
setException(ex);
return;
}
set(result);
} else {
releaseShared(0); // cancel
}
}
innerRun方法中果然还是调用了callable的call方法,并将结果赋给result变量
。
再看FutureTask的get方法:
public V get() throws InterruptedException, ExecutionException {
return sync.innerGet();
}
调用的是sync的innerGet方法:
V innerGet() throws InterruptedException, ExecutionException {
acquireSharedInterruptibly(0);
if (getState() == CANCELLED)
throw new CancellationException();
if (exception != null)
throw new ExecutionException(exception);
return result;
}
返回了result变量。