You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java多线程加载优化咨询:如何让排队请求仅执行必要加载?

优化Java异步加载逻辑的实现方案

原代码问题分析

  1. 同步执行阻塞返回:原实现中线程一调用loadAll()会同步执行DbLoadService.load(),无法满足“启动加载后立即返回”的需求。
  2. 重复加载无法释放:持有锁的线程会循环执行加载,只要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());
    }
}

逻辑验证

  1. 线程一(初始调用):检测到无当前任务,CAS设置第一次异步加载任务后立即返回,加载逻辑在后台线程执行。
  2. 线程二(第二次调用):检测到第一次加载任务未完成,等待其结束;随后CAS设置第二次加载任务,等待加载完成后返回。
  3. 线程三及以后:若第一次加载未完成则等待,完成后发现已有线程二启动的第二次加载任务,直接等待该任务完成后返回,复用线程二的加载流程。

额外说明

  • 异常处理可根据实际业务需求调整,比如增加重试机制或自定义异常抛出
  • 若业务场景不需要异步加载,可将CompletableFuture.runAsync替换为直接执行,但这样线程一会阻塞到加载完成,不符合“启动后立即返回”的初始需求。

内容的提问来源于stack exchange,提问作者Openeye

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 12:35:55