三分钟掌握 Runnable 和 Callable 异同
在 Java 多线程编程中,Runnable 和 Callable 是定义任务的两种核心接口。它们的区别如下表:
| 特性 | Runnable | Callable |
|---|---|---|
| 包路径 | java.lang |
java.util.concurrent |
| 方法签名 | void run() |
V call() throws Exception |
| 返回值 | 无返回值(void) |
可返回泛型类型结果(V) |
| 异常处理 | 不能抛出受检异常(需内部处理) | 可抛出受检异常(由调用者处理) |
| 使用场景 | 简单异步任务 | 需要结果返回或异常传递的复杂任务 |
| 线程池提交方式 | execute(Runnable) |
submit(Callable) → 返回 Future |
| 执行终止方式 | 仅通过中断标志位判断 | 可通过 Future.cancel() 终止 |
| Java 版本 | JDK 1.0+ | JDK 1.5+ |
代码示例对比
1. Runnable 典型用法
Runnable task = new Runnable() {
@Override
public void run() {
try {
System.out.println("执行任务");
} catch (Exception e) {
// 必须在此处理异常
}
}
};
// 线程池执行
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(task);
2. Callable 典型用法
Callable<Integer> task = new Callable<>() {
@Override
public Integer call() throws Exception {
if (Math.random() > 0.5) {
throw new IOException("模拟异常");
}
return 42;
}
};
// 提交任务并获取Future
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(task);
try {
Integer result = future.get(); // 阻塞获取结果
System.out.println("结果:" + result);
} catch (ExecutionException e) {
System.err.println("任务异常: " + e.getCause());
}
核心使用建议
-
选择时机:
- 需要任务结果 →
Callable - 需要抛出异常 →
Callable - 简单异步操作 →
Runnable
- 需要任务结果 →
-
返回值扩展:
// Runnable的变通方案(通过共享变量) AtomicInteger result = new AtomicInteger(); Runnable task = () -> result.set(100); -
FutureTask 转换:
// 将Runnable包装为Callable Callable<Void> callable = Executors.callable(runnableTask); // 将Runnable+结果包装为Callable Callable<String> callable = Executors.callable(runnableTask, "预设返回值");
结合 CompletableFuture 的现代用法
// 使用Callable获取结果
CompletableFuture.supplyAsync(() -> {
try {
return callableTask.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
});
// 使用Runnable忽略结果
CompletableFuture.runAsync(runnableTask);
高频面试题扩展
-
为什么要有两种接口?
Java早期版本(1.0)的线程模型需要简单任务接口,而 Java 5 引入并发库时需要支持更丰富的任务特性。 -
如何统一处理两种任务?
ExecutorService executor = ...; Future<?> future1 = executor.submit(runnableTask); Future<Integer> future2 = executor.submit(callableTask); -
性能差异?
两者本身无显著性能差异,但Callable的Future处理会带来轻微开销。

浙公网安备 33010602011771号