runnable不能返回值,因其run()方法返回void;callable通过call()支持泛型返回值和异常,需经executorservice提交并用future获取结果。

Runnable 不能返回值,这是接口设计决定的
Java 的 Runnable 接口只有一个 run() 方法,且返回类型是 void。这意味着你无论怎么写逻辑,都**无法从 run() 中 return 任何结果**,JVM 也不会提供获取它的机制。
常见错误是试图在 run() 里赋值给外部变量(比如 result = compute();),然后在主线程立刻读取——这有竞态风险,且不保证可见性(没加 volatile 或同步)。
- 别用
Runnable做需要结果的任务,哪怕只是临时存到AtomicReference里,也绕不开手动同步或内存可见性问题 -
Runnable适合“做了就完事”的场景:发日志、清缓存、触发通知、更新 UI 状态(配合 Handler/Looper) - 它被
Thread、ExecutorService.submit(Runnable)、ScheduledExecutorService等广泛支持,轻量无负担
Callable 是专为带返回值设计的替代方案
Callable 的 call() 方法声明为 V call() throws Exception,天然支持泛型返回值和受检异常。但它**不能直接交给 Thread 启动**,必须通过 ExecutorService 提交,由其包装成 Future 对象来取结果。
示例:
ExecutorService exec = Executors.newSingleThreadExecutor();
Future<integer> future = exec.submit(() -> {
Thread.sleep(1000);
return 42;
});
Integer result = future.get(); // 阻塞直到完成
exec.shutdown();</integer>
-
future.get()会阻塞,超时可用get(long, TimeUnit)避免无限等待 - 如果任务抛出异常,
get()会以ExecutionException包装再抛出,需注意异常链解析 -
Callable实例本身线程安全与否,和接口无关——仍需自己保证状态访问安全
别混用 Runnable 和 Callable 的提交方式
很多人以为 ExecutorService.submit(Runnable) 和 submit(Callable) 返回的 Future 行为一致,其实不然:
-
submit(Runnable)返回的Future调用get()永远返回null(不是异常,也不是默认值) -
submit(Runnable, result)是个特例:它允许你指定一个“完成时返回的固定值”,但这个值不是任务算出来的,而是你预先塞进去的 - 误把
Runnable当Callable用,又去检查future.get()是否非空,结果永远是null,容易误判任务失败
实际选型关键看“谁需要结果”和“何时要”
返回值不是目的,能被下游可靠消费才是重点。比如:
- 异步调用后要聚合多个结果 → 必须用
Callable + Future(或更现代的CompletableFuture) - 后台轮询设备状态,只更新本地字段 →
Runnable足够,加synchronized或ReentrantLock保护字段即可 - Web 请求中启一个线程预加载数据,但用户可能已跳转 → 用
Future.cancel(true)及时中断,这只有Callable提交后才能做到
真正容易被忽略的是:即使用了 Callable,如果任务内部有阻塞 I/O 或未响应中断,future.cancel(true) 也可能失效——call() 方法得主动检查 Thread.interrupted() 并退出。










