ThreadPoolExecutor的beforeExecute()参数及Callable转Runnable疑问
你观察得很到位!当你向ThreadPoolExecutor提交Callable任务时,它确实会隐式地将Callable转换为Runnable——而这个转换的核心就是FutureTask类。
为什么会有这个转换?
ThreadPoolExecutor的核心执行方法execute()只接受Runnable类型的参数,不管你提交的是Runnable还是Callable,最终都需要适配成Runnable才能被线程池执行。
当你调用submit(Callable<T>)方法时,线程池内部会做这几件事:
- 创建一个
FutureTask<T>实例,把你的Callable任务传入构造方法 - 调用
execute(futureTask)执行这个任务 - 返回这个FutureTask作为
Future<T>的实现,让你可以获取任务结果
而FutureTask本身实现了Runnable接口(同时也实现了Future接口),所以它完全符合execute()方法的参数要求。这就是为什么在beforeExecute()中拿到的是Runnable——它本质上是包装了你的Callable的FutureTask实例。
怎么在beforeExecute()中获取原始的Callable?
虽然FutureTask没有提供公共方法直接获取内部的Callable,但你可以通过反射来实现(注意反射可能会因为JDK版本变化而失效),或者在提交任务时自己做一层包装,比如:
// 自定义包装类,同时保存Callable和Runnable public class WrappedCallableTask<T> implements Runnable { private final Callable<T> callable; public WrappedCallableTask(Callable<T> callable) { this.callable = callable; } @Override public void run() { try { callable.call(); } catch (Exception e) { throw new RuntimeException(e); } } public Callable<T> getCallable() { return callable; } } // 提交时使用自定义包装类 executor.submit(new WrappedCallableTask<>(yourCallable)); // 在beforeExecute中获取 @Override protected void beforeExecute(Thread t, Runnable r) { super.beforeExecute(t, r); if (r instanceof WrappedCallableTask) { Callable<?> originalCallable = ((WrappedCallableTask<?>) r).getCallable(); // 处理原始Callable } }
为什么Executors没有提供Callable转Runnable的工具方法?
其实JDK不需要单独提供这个工具方法,因为FutureTask本身就是最标准的Callable转Runnable的实现——你只需要直接创建FutureTask实例,就完成了转换:
Callable<String> callable = () -> "Hello"; Runnable runnable = new FutureTask<>(callable);
而Executors里的callable(Runnable)方法是为了满足把无返回值的Runnable转换成带返回值的Callable的需求,这是另一个方向的适配,所以JDK提供了工具方法,但反向的转换直接用FutureTask就足够了,不需要额外封装。
内容的提问来源于stack exchange,提问作者chengdol

