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

日志阅读器中全局值被其他对象覆盖的原因排查

问题:日志阅读器中所有LogReader对象的错误字符串被最后一个值覆盖

场景说明

正在开发一款日志阅读器,用于统计自上次检查以来,YAML配置文件中指定错误的出现次数(已实现模拟日志写入,每5秒向日志文件写入一个指定错误)。

YAML配置示例:

logs:
  - path: path-to-file\logname.log
    error_names:
      - Error1t
      - Error2t
      - Error3t
    interval: 10

需要为每个错误创建独立的LogReader对象,并加入任务调度器循环执行,但出现异常:所有对象的错误字符串被最后传入的字符串覆盖。

相关代码

execute方法代码

public void execute() throws DfException, IOException, InterruptedException {
    int logFileAmount = config.getLogs().size();
    for(int logId = 0; logId < logFileAmount; logId++){

        var logConfig = config.getLogs().get(logId);
        var logReaderRerunTime = logConfig.getInterval() * 1000;
        var errors = logConfig.getErrorNames();

        for(int errorId = 0; errorId < errors.size(); errorId++) {
            var errorText = logConfig.getErrorNames().get(errorId);
            System.out.println(errorText);
            logReader = new LogReader(errorText, registry, config);
            taskScheduler.scheduleWithFixedDelay(() -> {
                try {
                    logReader.runLogReader();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }, Duration.ofMillis(logReaderRerunTime));
        }
    }
}

输出异常示例

Error11t
Error11t 0
Error22t
Error22t 0
Error33t
Error11t 0
Error33t 0
Error22t 0
Error33t 0


Error33t 0
Error33t 0
Error33t 0
Error33t 3
Error33t 3
Error33t 88

原因分析

问题出在lambda表达式对外部变量的捕获机制上:

  • 循环中使用了外部可变变量logReader,lambda捕获的是这个变量的引用,而非当前循环迭代时的对象实例。
  • 循环执行到最后一次迭代时,logReader会被赋值为最后一个LogReader对象,后续所有lambda任务执行时,都会调用这个最后一次赋值的对象的runLogReader()方法,导致所有任务都使用最后一个错误字符串。

解决方案

在循环内部创建局部的、独立的LogReader实例,让lambda捕获当前迭代的局部实例:

修改后的execute方法:

public void execute() throws DfException, IOException, InterruptedException {
    int logFileAmount = config.getLogs().size();
    for(int logId = 0; logId < logFileAmount; logId++){

        var logConfig = config.getLogs().get(logId);
        var logReaderRerunTime = logConfig.getInterval() * 1000;
        var errors = logConfig.getErrorNames();

        for(int errorId = 0; errorId < errors.size(); errorId++) {
            var errorText = logConfig.getErrorNames().get(errorId);
            System.out.println(errorText);
            // 创建局部变量,每个循环迭代持有独立的实例
            LogReader currentLogReader = new LogReader(errorText, registry, config);
            taskScheduler.scheduleWithFixedDelay(() -> {
                try {
                    currentLogReader.runLogReader();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }, Duration.ofMillis(logReaderRerunTime));
        }
    }
}

这样每个lambda都会捕获当前循环迭代对应的currentLogReader实例,不会被后续循环的赋值覆盖,每个任务都会使用自己对应的错误字符串执行统计。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 06:43:21