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

Java使用并发List仍保留请求数据的原因及递归场景下的解决方法

问题解决思路

问题根源

  1. 你当前定义的menuDTOList是Demo类的实例成员变量,只要Demo类实例没有被销毁,列表中存储的数据就会一直留存,多次调用getData方法都会往同一个列表中追加数据,自然会保留上一次请求的残留内容。
  2. Collections.synchronizedList仅用来解决多线程并发修改列表的线程安全问题,完全不解决多次请求复用同一个列表实例的问题,所以对你的场景无效。
  3. 如果你在递归调用过程中清空列表,会把当前请求已经加入列表的有效数据也一并清除,自然无法正常生效。

最优解决方案(推荐)

将列表改为递归方法的入参传递,每次请求调用getData时新建空列表,再传递给递归方法,完全不依赖类成员变量,既可以保留原有递归逻辑,还能天然隔离不同请求的数据,没有历史数据残留问题。

修改后的代码如下:

public class Demo {

    // 移除原有的成员变量menuDTOList
    protected Iterable<MenuDTO> getData() {
        // 每次请求新建独立的空列表,不同请求完全隔离
        List<MenuDTO> menuDTOList = Collections.synchronizedList(new ArrayList<MenuDTO>());
        for (...) {
            // 调用methodA时传入当前请求的列表
            methodA(你的menuItemExpandedDTO实例, menuDTOList);
        }
        return menuDTOList;
    }

    // 新增List<MenuDTO>类型的入参
    private void methodA(final MenuItemExpandedDTO menuItemExpandedDTO, List<MenuDTO> menuDTOList) {
        menuDTOList.add(dto);
        for (...) {
            // 调用methodB时传入当前请求的列表
            methodB(menuDTOList);
        }
    }

    // 新增List<MenuDTO>类型的入参
    private void methodB(List<MenuDTO> menuDTOList) {
        for (...) {
            // 调用methodA时传入当前请求的列表
            methodA(dto, menuDTOList);
        }
    }
}

该方案优势:

  • 不存在历史数据残留问题,每次请求都是独立的新列表
  • 天然线程安全,多个请求同时调用也不会互相干扰
  • 不需要额外执行清空操作,不会影响原有递归逻辑

备选方案(仅适用于无法修改递归方法入参的场景)

如果受限于现有代码规范无法修改methodA和methodB的入参,可以用ThreadLocal存储每个线程独立的列表,每次请求初始化新列表,用完手动清除避免内存泄漏,代码如下:

public class Demo {
    // 用ThreadLocal存储每个线程独立的列表实例
    private ThreadLocal<List<MenuDTO>> menuDTOListHolder = ThreadLocal.withInitial(ArrayList::new);

    protected Iterable<MenuDTO> getData() {
        try {
            // 每次请求先清空当前线程的列表
            menuDTOListHolder.get().clear();
            for (...) {
                methodA(你的menuItemExpandedDTO实例);
            }
            // 返回列表拷贝,避免后续修改影响返回结果
            return new ArrayList<>(menuDTOListHolder.get());
        } finally {
            // 用完手动移除,避免内存泄漏
            menuDTOListHolder.remove();
        }
    }

    private void methodA(final MenuItemExpandedDTO menuItemExpandedDTO) {
        menuDTOListHolder.get().add(dto);
        for (...) {
            methodB();
        }
    }

    private void methodB() {
        for (...) {
            methodA(dto);
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 18:45:07