Java如何确保getContent方法同一时间仅一个请求调用Web服务?
这是一个典型的并发请求合并场景,核心是让多个并发请求共享同一个远程调用结果,避免重复发起请求。我给你写一个线程安全的Java实现,完全匹配你的需求:
完整代码实现
import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; public class ContentService { // 原子引用保存当前正在执行的请求Future,保证多线程下的线程安全 private final AtomicReference<CompletableFuture<Response>> currentExecutingFuture = new AtomicReference<>(); public Response getContent() throws MyException { while (true) { CompletableFuture<Response> existingFuture = currentExecutingFuture.get(); if (existingFuture != null) { // 已有请求在处理,直接等待其结果 try { return existingFuture.join(); } catch (Exception e) { // 将底层异常转换为自定义异常抛出 throw new MyException("等待请求结果失败", e); } } // 没有正在执行的请求,创建新的Future并尝试通过CAS设置到原子引用中 CompletableFuture<Response> newRequestFuture = new CompletableFuture<>(); if (currentExecutingFuture.compareAndSet(null, newRequestFuture)) { try { // 实际发起Web服务调用 Response webResponse = invokeRemoteWebService(); // 完成Future,通知所有等待的线程 newRequestFuture.complete(webResponse); return webResponse; } catch (Exception e) { // 标记Future执行失败,通知所有等待的线程 newRequestFuture.completeExceptionally(e); throw new MyException("Web服务调用失败", e); } finally { // 无论成功失败,都清空原子引用,让后续请求可以发起新的调用 currentExecutingFuture.compareAndSet(newRequestFuture, null); } } // CAS失败意味着其他线程已经抢先发起了请求,回到循环重新获取现有Future } } // 模拟实际的Web服务调用,替换成你的真实调用逻辑(比如RestTemplate、Feign等) private Response invokeRemoteWebService() throws Exception { // 模拟调用耗时 Thread.sleep(1000); return new Response("从Web服务获取的内容"); } } // 自定义异常类(根据你的实际需求调整) class MyException extends Exception { public MyException(String message, Throwable cause) { super(message, cause); } } // 响应类(根据你的实际需求调整) class Response { private String content; public Response(String content) { this.content = content; } // getter、setter等方法 public String getContent() { return content; } }
关键逻辑解释
- AtomicReference + CAS操作:用
AtomicReference保存当前正在执行的请求Future,通过compareAndSet(CAS)原子操作保证多线程下只有一个线程能发起Web服务调用,避免重复请求。 - CompletableFuture共享结果:所有后续到达的请求会等待第一个请求对应的Future完成,无论成功还是失败,结果都会同步给所有等待的线程。
- 自动清理机制:在
finally块中清空原子引用,确保当前请求处理完成后,新的请求可以正常发起新的Web服务调用,不会一直复用旧的结果。 - 异常统一处理:无论是Web服务调用本身的异常,还是等待过程中的异常,都会转换为自定义的
MyException抛出,保证上层调用的异常一致性。
这个实现完全满足你的需求:第一个请求触发Web服务调用,后续请求等待结果;请求完成(成功/失败)后,所有等待线程同步结果;新的请求到来时会重新发起新的Web服务调用。
内容的提问来源于stack exchange,提问作者Addev
相关产品推荐
相关产品推荐

