Callable比Runnable多返回结果和抛出受检异常的能力;其call()方法必须有返回值且可声明throws任何异常,而Runnable的run()方法返回void且不能抛出受检异常。

Callable比Runnable多什么能力
Callable能返回结果、抛出受检异常,而Runnable不能。这是最根本的区别——如果你的任务需要计算一个值(比如查数据库后返回ID),或者可能遇到IOException这类必须处理的异常,就该用Callable,而不是硬套Runnable。
注意:Callable的泛型参数就是返回类型,比如Callable表示执行后返回String;而Runnable.call()不存在,它的方法叫run()且无返回值。
-
Callable.call()方法必须有返回值,且可声明 throws 任何异常 -
Runnable.run()方法返回 void,不能抛出受检异常 - 直接 new Thread(new Runnable()) 可以启动,但 new Thread(new Callable()) 编译不通过——Callable 不能直接交给 Thread
Future.get() 为什么会阻塞,怎么避免卡死
Future.get() 是同步等待结果的操作,调用时线程会挂起,直到任务完成或超时。常见错误是没设超时,结果依赖的服务响应慢,整个线程被拖住。
推荐始终使用带超时的重载:future.get(3, TimeUnit.SECONDS)。超时后抛出TimeoutException,你可以降级、重试或记录告警,而不是让线程无限等下去。
立即学习“Java免费学习笔记(深入)”;
- 不带参数的
get()可能永远阻塞(比如任务死循环或未提交) - 超时单位别写错:
TimeUnit.MILLISECONDS和TimeUnit.SECONDS差1000倍 - 如果任务已取消,
get()会抛CancellationException,需单独捕获
try {
String result = future.get(2, TimeUnit.SECONDS);
System.out.println(result);
} catch (TimeoutException e) {
System.err.println("任务超时,执行降级逻辑");
} catch (ExecutionException e) {
Throwable cause = e.getCause();
System.err.println("任务执行异常:" + cause.getClass().getSimpleName());
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断状态
}
submit(Callable) 返回的 Future 能取消吗
可以,但是否真正生效取决于任务当前状态和实现方式。future.cancel(true) 的 true 表示「尝试中断正在运行的线程」,但这只是个提示——线程是否响应中断,由任务自己决定。
婚纱影楼小程序提供了一个连接用户与影楼的平台,相当于影楼在微信的官网。它能帮助影楼展示拍摄实力,记录访客数据,宣传优惠活动。使用频率高,方便传播,是影楼在微信端宣传营销的得力助手。功能特点:样片页是影楼展示优秀摄影样片提供给用户欣赏并且吸引客户的。套系页是影楼根据市场需求推出的不同套餐,用户可以按照自己的喜好预定套系。个人中心可以查看用户预约的拍摄计划,也可以获取到影楼的联系方式。
比如你在call()里写了while (!Thread.currentThread().isInterrupted())并定期检查,那中断才有效;如果任务在做纯 CPU 计算且没检查中断,cancel 就不会停止它。
- 任务还没开始:cancel 成功,状态变为
isCancelled() == true - 任务正在运行:仅当线程响应中断(如调用
Thread.sleep()、BlockingQueue.take()等可中断方法)才会退出 - 任务已完成:cancel 失败,
isCancelled()返回 false,isDone()返回 true
为什么常用 ExecutorService.submit(Callable) 而不是直接 new FutureTask
直接 new FutureTask 是可行的,但它要你手动管理线程(比如传给new Thread(task).start()),容易漏掉异常处理、线程复用、资源关闭等问题。而ExecutorService.submit() 内部封装了这些,还统一了生命周期管理。
更重要的是:ExecutorService 支持批量提交、优雅关闭(shutdownNow() 会尝试 cancel 所有未完成的 Future)、以及线程池复用——这对高频小任务很关键。
-
FutureTask是Runnable和Future的组合,适合需要手动控制执行时机的场景(比如延迟触发) - 日常异步任务,优先走
executor.submit(new MyCallable()),别绕路 - 别忘了在应用退出前调用
executor.shutdown()或shutdownNow(),否则 JVM 可能不退出
真正容易被忽略的点是:Future 不代表任务一定在执行中——它只是一张“凭证”,背后任务可能已被拒绝、取消、或甚至还没排上队。判断状态得靠 isDone()、isCancelled() 配合 get() 的异常类型,不能光看 future 对象是否存在。









