在Java并发编程里,Runnable的run方法没有返回值,也不允许抛出受检异常,这导致很多需要拿到计算结果的场景无法直接用它能解决。Callable接口的出现弥补了这个缺陷,它定义的call方法可以返回结果并抛出异常,配合线程池与Future就能实现带返回值的异步任务。

Callable接口的基本用法
Callable是一个泛型接口,只有一个call方法,类型参数就是返回值的类型。下面定义一个简单任务,模拟耗时计算后返回字符串。
import java.util.concurrent.Callable;
public class SumTask implements Callable<Integer> {
private final int n;
public SumTask(int n) {
this.n = n;
}
@Override
public Integer call() throws Exception {
// 模拟耗时计算:求1到n的累加和
int sum = 0;
for (int i = 1; i <= n; i++) {
sum += i;
Thread.sleep(10);
}
return sum;
}
}
通过线程池提交并获取Future
通常我们用ExecutorService来提交Callable任务,submit方法会立即返回一个Future对象,它代表异步计算的结果。调用get方法时会阻塞直到任务完成。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class CallableDemo {
public static void main(String[] args) throws Exception {
ExecutorService pool = Executors.newSingleThreadExecutor();
SumTask task = new SumTask(100);
Future<Integer> future = pool.submit(task);
System.out.println("任务已提交,主线程继续做其他事");
// 阻塞获取结果
Integer result = future.get();
System.out.println("计算结果: " + result);
pool.shutdown();
}
}
设置超时避免无限等待
如果任务执行过久,可以用带超时参数的get方法,防止主线程一直卡住。
import java.util.concurrent.TimeUnit;
try {
// 最多等待3秒
Integer r = future.get(3, TimeUnit.SECONDS);
System.out.println("带超时结果: " + r);
} catch (java.util.concurrent.TimeoutException e) {
System.out.println("任务超时,取消执行");
future.cancel(true);
}
处理任务中的异常
Callable可以抛出受检异常,这些异常会在调用get时包装成ExecutionException抛出,需要通过getCause拿到原始异常。
import java.util.concurrent.ExecutionException;
try {
future.get();
} catch (ExecutionException e) {
Throwable cause = e.getCause();
System.out.println("任务执行失败: " + cause.getMessage());
}
Future常用方法总结
| 方法 | 说明 |
|---|---|
| get() | 阻塞直到任务完成并返回结果 |
| get(long, TimeUnit) | 在指定时间内等待结果,超时抛异常 |
| cancel(boolean) | 尝试取消任务,参数为true表示中断运行中的线程 |
| isDone() | 判断任务是否已结束 |
| isCancelled() | 判断任务是否在完成前被取消 |
小结
使用Callable和Future,我们可以把耗时操作放到后台线程执行,主线程在需要时再拿结果,既清晰又安全。实际项目中建议用线程池管理任务,并合理设置超时与异常处理逻辑。