基于CompletableFuture实现API同步异步方法的签名设计疑问
核心差异是方法执行的线程模型,而非用户后续对结果的处理方式
首先你第一个疑问的答案是肯定的:只要返回CompletableFuture,用户确实可以自行选择处理结果的方式:调用.get()/.join()阻塞等待结果,或者用.thenAcceptAsync()/.whenComplete()等链式方法异步处理结果。
但即使所有方法都返回CompletableFuture,invoke和invokeAsync的设计依然有意义,二者的本质区别是服务方法本身的执行时机、是否阻塞调用线程:
invoke方法:会在当前调用线程同步执行目标服务方法,等方法执行完成后,再把结果封装为已经完成的CompletableFuture返回。你调用invoke的瞬间,当前线程就会被阻塞,直到服务方法执行结束。invokeAsync方法:会把目标服务方法提交到异步线程池执行,调用后立刻返回未完成的CompletableFuture,不会阻塞当前调用线程,服务方法的执行逻辑全程在独立的异步线程运行。
可以通过下面的示例直观感受差异:
// 假设test方法执行需要耗时10s // 调用同步实现的invoke long start1 = System.currentTimeMillis(); CompletableFuture<String> res1 = MyRegistry.getInstance().invoke(service, "test"); System.out.println("invoke调用耗时:" + (System.currentTimeMillis() - start1)); // 输出约10000,调用时已经同步执行完服务方法 res1.get(); // 立刻返回,无额外阻塞 // 调用异步实现的invokeAsync long start2 = System.currentTimeMillis(); CompletableFuture<String> res2 = MyRegistry.getInstance().invokeAsync(service, "test"); System.out.println("invokeAsync调用耗时:" + (System.currentTimeMillis() - start2)); // 输出<100,调用时仅提交了异步任务 res2.get(); // 这里才会阻塞等待约10s
设计建议
你提到的建议签名有两个可优化点:
- 不要用裸类型
CompletableFuture,要保留原有泛型设计,改为public <T> CompletableFuture<T>,避免用户后续强转结果的冗余操作和类型安全问题 - 如果要兼容老业务代码,更合理的设计是保留原有返回泛型
T的同步invoke方法,仅新增返回CompletableFuture<T>的invokeAsync方法,老业务逻辑无需修改,新业务可以按需用异步能力。如果要全量统一返回CompletableFuture,则要在方法注释里明确标注两个方法的线程模型差异,避免用户误用。
如果全量统一返回CompletableFuture后,没有明确的线程模型差异,那invoke和invokeAsync的命名确实没有存在必要,保留其中一套即可,或者新增入参控制执行策略即可。
内容的提问来源于stack exchange,提问作者bobbyrne01
相关产品推荐
相关产品推荐

