Java线程池submit能统一处理Runnable和Callable,核心在于内部适配:Runnable通过RunnableFuture(如FutureTask)封装为带null或指定结果的Future,Callable则直接封装为可返回泛型结果的FutureTask;三个重载方法分别支持Runnable、Runnable+result、Callable任务,均返回Future以便统一管理执行状态与结果。

Java线程池的 submit 方法能统一处理 Runnable 和 Callable,核心在于它内部做了适配封装——Runnable 被自动包装成带默认结果的 RunnableFuture,而 Callable 则直接封装为可获取结果的 FutureTask。你不需要手动转换,只需按需传入,线程池会帮你搞定后续异步执行与结果管理。
submit 接收哪几种任务类型
ThreadPoolExecutor 和 ExecutorService 提供了三个重载的 submit 方法:
-
Future<?> submit(Runnable task):提交无返回值任务,返回Future<Void>(实际是FutureTask<Void>),调用get()得到null -
<T> Future<T> submit(Runnable task, T result):提交 Runnable,但指定一个“完成时返回的结果”,get()将返回该result值 -
<T> Future<T> submit(Callable<T> task):提交有返回值、可抛异常的任务,get()返回call()的计算结果
提交 Runnable 的实际写法
即使 Runnable 没有返回值,submit 仍能接收,并返回 Future 对象。这让你可以统一用 get() 等待执行结束,或用 isDone() 查询状态:
ExecutorService pool = Executors.newFixedThreadPool(2);
Future<?> future = pool.submit(() -> {
System.out.println("正在执行...");
try { Thread.sleep(1000); } catch (InterruptedException e) {}
System.out.println("执行完毕");
});
future.get(); // 阻塞等待完成(返回 null)
如果想让 Runnable “假装”有返回值,用带 result 的重载更直观:
立即学习“Java免费学习笔记(深入)”;
Future<String> f = pool.submit(() -> {
System.out.println("上传文件中...");
}, "上传成功");
System.out.println(f.get()); // 输出:上传成功
提交 Callable 的标准流程
Callable 更适合需要结果或异常反馈的场景。它的 call() 方法可返回泛型值、抛出受检异常,这些都会被 Future.get() 捕获:
- 正常执行:
get()返回call()的返回值 - 抛出异常:
get()抛出ExecutionException,原始异常在getCause()中 - 任务被取消:
get()抛出CancellationException
Future<Integer> future = pool.submit(() -> {
int sum = 0;
for (int i = 1; i <= 100; i++) sum += i;
return sum; // 返回 Integer
});
System.out.println(future.get()); // 输出:5050
关键注意事项
使用 submit 时要注意几个易错点:
- 无论提交 Runnable 还是 Callable,返回的都是
Future,但只有调用get()才会真正阻塞并获取结果(或异常) - 不调用
get()或cancel(),Future 对象本身不会释放资源;长期堆积未处理的 Future 可能引发内存压力 - submit 不会立即抛出任务内的运行时异常,异常会被“静默”包裹进 Future,直到 get() 被调用才暴露
- 务必在用完后调用
shutdown()或shutdownNow(),否则 JVM 不会退出(线程池持有非守护线程)


















