Java多线程加载优化咨询:如何让排队请求仅执行必要加载?
优化Java异步加载逻辑的实现方案
原代码问题分析
- 同步执行阻塞返回:原实现中线程一调用
loadAll()会同步执行DbLoadService.load(),无法满足“启动加载后立即返回”的需求。 - 重复加载无法释放:持有锁的线程会循环执行加载,只要
isLoadPending被标记为true就重复执行,导致不必要的多次加载,线程无法及时释放。
优化思路
基于需求中的三个调用场景,我们需要实现:
- 线程一异步启动加载后立即返回
- 线程二等待线程一加载完成后,自行启动一次加载并等待完成返回
- 后续线程复用线程二的加载流程,同步返回
核心设计点:
- 用
AtomicReference跟踪当前加载任务,保证多线程下的状态可见性与原子性 - 利用
CompletableFuture实现异步加载,同时支持线程等待任务完成
优化后代码实现
import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; public class LoadService { // 跟踪当前加载任务:null=无任务,任务实例=正在执行的加载 private final AtomicReference<CompletableFuture<Void>> currentTask = new AtomicReference<>(); public void loadAll() { while (true) { CompletableFuture<Void> task = currentTask.get(); if (task == null) { // 启动第一次异步加载(线程一逻辑) CompletableFuture<Void> firstLoad = CompletableFuture.runAsync(DbLoadService::load); if (currentTask.compareAndSet(null, firstLoad)) { // 线程一启动加载后立即返回 return; } // CAS失败,说明其他线程已启动任务,进入下一轮循环 } else if (!task.isDone()) { // 当前有加载任务在执行,等待任务完成(线程二、三等待线程一的逻辑) try { task.join(); } catch (Exception e) { throw new RuntimeException("加载执行失败", e); } // 任务完成后进入下一轮循环,处理后续逻辑 } else { // 第一次加载已完成,尝试启动第二次加载(线程二逻辑) CompletableFuture<Void> secondLoad = CompletableFuture.runAsync(DbLoadService::load); if (currentTask.compareAndSet(task, secondLoad)) { // 线程二等待自己启动的加载完成后返回 try { secondLoad.join(); } catch (Exception e) { throw new RuntimeException("二次加载执行失败", e); } // 二次加载完成,重置任务状态 currentTask.set(null); return; } else { // 其他线程已启动二次加载,等待其完成(线程三及以后逻辑) CompletableFuture<Void> existingSecondLoad = currentTask.get(); if (existingSecondLoad != null && !existingSecondLoad.isDone()) { try { existingSecondLoad.join(); } catch (Exception e) { throw new RuntimeException("二次加载执行失败", e); } currentTask.set(null); } return; } } } } } // 模拟数据库加载服务 class DbLoadService { public static void load() { // 替换为实际的数据库加载逻辑 try { Thread.sleep(1000); // 模拟加载耗时 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("加载完成,执行线程:" + Thread.currentThread().getName()); } }
逻辑验证
- 线程一(初始调用):检测到无当前任务,CAS设置第一次异步加载任务后立即返回,加载逻辑在后台线程执行。
- 线程二(第二次调用):检测到第一次加载任务未完成,等待其结束;随后CAS设置第二次加载任务,等待加载完成后返回。
- 线程三及以后:若第一次加载未完成则等待,完成后发现已有线程二启动的第二次加载任务,直接等待该任务完成后返回,复用线程二的加载流程。
额外说明
- 异常处理可根据实际业务需求调整,比如增加重试机制或自定义异常抛出
- 若业务场景不需要异步加载,可将
CompletableFuture.runAsync替换为直接执行,但这样线程一会阻塞到加载完成,不符合“启动后立即返回”的初始需求。
内容的提问来源于stack exchange,提问作者Openeye
相关产品推荐
相关产品推荐

