Groovy中future.get()返回null问题排查与咨询
Groovy异步工具类Future.get()返回null的问题分析
问题场景
开发表格合并的Groovy项目时,AsyncUtils工具类中future.get()始终返回null,JUnit测试用例断言失败。工具类及测试代码如下:
AsyncUtils工具类代码
package com.signaturemd.sposZohoMergeScript.utils import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import java.util.concurrent.Future public final class AsyncUtils { private static ExecutorService threadPool; public static <T> Future<T> StartAsyncTask(Closure<T> onTask) { return this.GetThreadPool() .submit(onTask); } public static <T> T AwaitResult(Future<T> future) { return future.get(); } public static ExecutorService GetThreadPool() { if (!this.threadPool) this.threadPool = Executors.newCachedThreadPool(); return this.threadPool; } }
JUnit测试代码
package com.signaturemd.sposZohoMergeScript.utils; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; class AsyncUtilsTest { @Test void testAwaitResult() { final List promises = [ { Thread.sleep(100); return "hello"; }, { Thread.sleep(50); return "world"; }, ].collect { Closure onTask -> return AsyncUtils.StartAsyncTask(onTask) } promises.each { promise -> final String result = AsyncUtils.AwaitResult(promise); assert result != null; } } }
问题原因
核心问题是Groovy闭包被错误识别为Runnable而非Callable:
- ExecutorService的
submit方法有两个重载:一个接受Runnable(无返回值),一个接受Callable<T>(有返回值)。 - 直接传递Groovy闭包时,若未显式指定类型,Groovy会默认适配为
Runnable,此时run()方法无返回值,导致Future.get()返回null。 - 工具类虽声明了
Closure<T>,但提交时未显式转换为Callable<T>,JVM自动选用了Runnable重载。
解决方案
修改StartAsyncTask方法,显式将闭包转换为Callable<T>,确保调用接受Callable的submit重载:
public static <T> Future<T> StartAsyncTask(Closure<T> onTask) { return this.GetThreadPool() .submit(onTask as Callable<T>); }
Groovy支持通过as关键字将Closure转换为Callable接口,这样闭包的返回值会被正确传递,Future.get()就能拿到预期结果。
关于等待所有Futures的问题
当前测试中逐个调用AwaitResult(即future.get())的方式可行,因为get()是阻塞式方法,会等待当前Future执行完成后再处理下一个。如果合并工作需要所有异步任务结果就绪才能执行,确实需要等待所有Futures完成——你可以保持当前逐个等待的方式,也可以用ExecutorService.invokeAll()一次性提交所有任务并等待全部完成,核心是先解决闭包的Callable适配问题。
内容的提问来源于stack exchange,提问作者Mike Warren
相关产品推荐
相关产品推荐

