Callable
Callable
接口的定义:

- 泛型类型
V
:指定了call()
方法返回值的类型。Callable
的call()
方法会返回一个类型为V
的值。 call()
方法 :与Runnable
的run()
方法类似,call()
方法包含了任务的执行逻辑。不同的是,call()
方法可以返回一个结果 (通过返回值V
),并且它可以抛出异常
与 Runnable
的对比
特性 | Runnable |
Callable |
---|---|---|
返回值 | run() 方法没有返回值 |
call() 方法有返回值 |
异常处理 | run() 方法不能抛出任何异常 |
call() 方法可以抛出异常 |
使用场景 | 用于不需要返回值或异常处理的场景 | 用于需要返回值或可能抛出异常的场景 |
Callable
的工作原理
Callable
的原理是基于 线程池 和 任务提交机制 ,尤其是 ExecutorService
,可以通过 ExecutorService.submit()
方法提交任务,并获取 Future
对象。Callable
的执行过程大致如下:
- 任务提交 :将
Callable
任务提交给一个线程池(例如,ExecutorService
)。线程池会创建一个新的线程(或复用现有的空闲线程)来执行该任务。 - 任务执行 :线程池中的线程会调用
Callable.call()
方法执行任务的具体操作。 - 返回结果 :任务执行完成后,
call()
方法会返回一个结果值(类型为V
)。如果call()
方法抛出了异常,异常会被捕获并封装在Future
中。 - 获取结果 :使用
Future.get()
方法可以获取Callable
执行的返回值。Future.get()
是一个阻塞方法,直到任务执行完毕并返回结果。
Callable
与 Future
的配合
java
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask futureTask = new FutureTask<>(new MyThread());
new Thread(futureTask).start();
Object o = futureTask.get();
System.out.println(o);
}
}
class MyThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("callable");
return 123;
}
}
如何在Thread
中使用Callable
?
这就需要callable
与Future
的配合使用了,我们查看源码可以看到

Callable
原本与Thread
没有联系的,是通过RunnableFuture
接口建立了联系;
为了使 Callable
可以与 Thread
配合使用,java.util.concurrent
包中引入了 RunnableFuture
接口,它继承了 Runnable
和 Future
接口:

Runnable
接口使得RunnableFuture
可以在Thread
中被执行。Future
接口提供了获取结果和取消任务的能力。
于是在new Thread()
里边就可以使用Callable
。
FutureTask
实现
RunnableFuture
接口的实现类是 FutureTask
,它实现了 Callable
、Runnable
和 Future
,这就是 Callable
和 Thread
之间的桥梁。FutureTask
可以包装一个 Callable
实例并通过 run()
方法在 Thread
中执行。

但是有两个问题
1.Future.get()
异常处理
- 如果
Callable
中的call()
方法抛出了异常,Future.get()
会 包装 这个异常并重新抛出。 - 异常类型为
ExecutionException
,可以通过getCause()
获取原始的异常。
不过这个 get
是阻塞的,需要等待获取到里边的内容才会往下继续进行;所以一般会将它放到代码的最后一步执行,防止阻塞线程;或是异步处理;
2.结果会被缓存
java
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask futureTask = new FutureTask<>(new MyThread());//适配类
new Thread(futureTask, "A").start();
new Thread(futureTask, "B").start();
Object o = futureTask.get();
System.out.println(o);
}
}
class MyThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("callable");
return 123;
}
}
在这个代码中 你觉得"callable" 会打印几次?

结果只是打印了一次,因为FutureTask
有一个非常重要的特性:它缓存任务的执行结果。即使同一个 FutureTask
被多个线程共享执行,它的结果也 只会被计算一次 ,之后所有的线程都会获得相同的结果。

通过源码可以得知,是否已经被缓存了。