callable 与 runnable 的核心区别在于:callable 的 call() 可抛受检异常且返回泛型结果,必须通过 executorservice.submit() 提交并返回 future;而 runnable 的 run() 不能抛受检异常、无返回值,可直接传给 thread 启动。

Callable 和 Runnable 的关键区别在哪
根本不是“能不能返回值”这么简单——Callable 的 call() 方法能抛出受检异常,而 Runnable 的 run() 不能;更实际的是,Callable 必须配合 ExecutorService 使用,它不直接交给 Thread 启动。
常见错误现象:new Thread(new MyCallable()).start() 编译失败,因为 Thread 构造器只接受 Runnable。
-
Callable是函数式接口,但返回类型是泛型V,比如Callable<string></string> - 不能直接 new Thread,必须用
executor.submit()提交,返回Future - 如果任务逻辑里有 I/O 或计算密集操作,
Callable+Future是比轮询标志位更干净的异步结果获取方式
submit() 返回的 Future 怎么安全取结果
Future.get() 是阻塞调用,不加超时会卡死线程;而且一旦任务抛异常,get() 会包装成 ExecutionException 再抛出,原异常藏在 getCause() 里。
使用场景:你真正需要结果才等,比如异步查数据库后拼装响应;如果只是“发完就不管”,其实不该调 get()。
- 永远优先用
future.get(3, TimeUnit.SECONDS)带超时,避免无限等待 - 捕获
TimeoutException、CancellationException、ExecutionException三类异常 -
ExecutionException.getCause()才是你call()方法里 throw 的原始异常 - 调用
get()前建议先isDone()判断,但别用来轮询——那是反模式
为什么 Executors.newFixedThreadPool 要慎用
默认队列是无界 LinkedBlockingQueue,任务提交速度 > 消费速度时,内存会持续增长直到 OOM;JDK 8+ 的 ForkJoinPool.commonPool() 更轻量,但不适用于阻塞型任务(比如带数据库连接的 Callable)。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
性能影响:线程数配少了,Future.get() 等得久;配多了,上下文切换开销大,还可能压垮下游服务。
- 生产环境推荐用
new ThreadPoolExecutor(core, max, keepAlive, unit, new ArrayBlockingQueue(100)) - 拒绝策略别用默认的
AbortPolicy(直接抛异常),考虑CallerRunsPolicy让提交线程自己执行 - 务必显式调用
executor.shutdown(),否则 JVM 不会退出——这是最常被漏掉的一行
CompletableFuture 替代 Future 的真实收益
不是“更新潮”,而是解决了 Future 的三个硬伤:无法链式处理结果、无法组合多个异步任务、无法指定回调线程池。
兼容性影响:JDK 8+ 才有,如果你的项目还在用 JDK 7,这条路走不通;但只要能升,CompletableFuture 几乎总比裸 Future 更稳。
-
supplyAsync(() -> doHeavyWork(), executor)直接封装Callable逻辑 -
thenApply()处理结果、exceptionally()捕获异常,不用手动 try-catchget() -
thenCompose()串行异步任务,thenCombine()并行合并两个Future结果 - 注意
thenAccept()默认用ForkJoinPool,IO 任务记得传自定义executor
复杂点在于异常传播路径变长,一个 exceptionally() 只捕获上游直接异常,嵌套深处的异常容易漏;最容易被忽略的是,忘记对 CompletableFuture 调用 join() 或 get(),导致主线程提前退出,后台任务没执行完。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










