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

构造函数已赋值,为何HashMap在run()方法中为空?

DirWatcher的files HashMap在Timer调度时为空的问题排查与解决

问题核心

开发配置处理器时,DirWatcher类的files HashMap在构造函数初始化后明明有值,但Timer调度执行run()方法时却为空,导致所有文件被误判为新增操作。

可能的原因及解决步骤

1. 子类隐藏了父类的files变量

如果你的匿名DirWatcher实例中重新定义了同名的files HashMap,会覆盖父类的变量——构造函数初始化的是父类的files,但run()方法访问的是子类的空变量。

解决:检查匿名子类代码,确保没有重复定义files变量。父类中定义protected Map<File, Long> files,子类直接使用即可,不要重新声明。

2. 线程可见性问题

Timer的run()方法在独立线程执行,普通HashMap不保证线程间的可见性,构造函数中写入的files数据可能无法被run()所在线程读取到。

解决:

  • 用volatile修饰files变量,保证变量的跨线程可见性;
  • 替换为线程安全的ConcurrentHashMap,避免并发修改问题:
    protected volatile Map<File, Long> files = new ConcurrentHashMap<>();
    

3. 实例不一致

可能Timer调度的不是你初始化的那个DirWatcher实例,比如代码中不小心创建了多个实例。

验证:在构造函数和run()方法中打印实例的哈希值,确认是否为同一个对象:

// 构造函数中
System.out.println("DirWatcher实例哈希: " + this.hashCode());
// run()方法中
System.out.println("运行的实例哈希: " + this.hashCode());

如果两个哈希值不同,说明调度的是另一个实例,检查ConfigHandler中创建和调度任务的代码,确保只创建一个DirWatcher实例并交给Timer调度。

4. 初始化逻辑异常(排除项)

虽然你说构造后日志显示有值,但可以再确认初始化代码是否正确:

  • 检查getConfigFiles()方法是否正确过滤出JSON文件;
  • 确保遍历目录时没有抛出未捕获的异常,导致初始化中断。

修正后的示例代码

DirWatcher抽象类

public abstract class DirWatcher extends TimerTask {
    protected volatile Map<File, Long> files = new ConcurrentHashMap<>();
    private final File configDir;

    public DirWatcher(String dirPath) {
        this.configDir = new File(dirPath);
        List<File> configFiles = getConfigFiles();
        for (File f : configFiles) {
            files.put(f, f.lastModified());
            System.out.println("初始化添加文件: " + f.getName() + ",实例哈希: " + this.hashCode());
        }
    }

    protected List<File> getConfigFiles() {
        List<File> result = new ArrayList<>();
        if (configDir.isDirectory()) {
            File[] files = configDir.listFiles((dir, name) -> name.endsWith(".json"));
            if (files != null) {
                result.addAll(Arrays.asList(files));
            }
        }
        return result;
    }

    @Override
    public void run() {
        System.out.println("运行实例哈希: " + this.hashCode());
        System.out.println("当前files大小: " + files.size());

        List<File> currentFiles = getConfigFiles();
        // 检查新增/修改
        for (File f : currentFiles) {
            Long storedModified = files.get(f);
            if (storedModified == null) {
                handleAdd(f);
                files.put(f, f.lastModified());
            } else if (f.lastModified() > storedModified) {
                handleModify(f);
                files.put(f, f.lastModified());
            }
        }
        // 检查删除
        Set<File> storedFiles = new HashSet<>(files.keySet());
        storedFiles.removeAll(currentFiles);
        for (File f : storedFiles) {
            handleDelete(f);
            files.remove(f);
        }
    }

    protected abstract void handleAdd(File file);
    protected abstract void handleModify(File file);
    protected abstract void handleDelete(File file);
}

ConfigHandler中的调度代码

public class ConfigHandler {
    public void startConfigWatcher() {
        Timer timer = new Timer();
        DirWatcher watcher = new DirWatcher("/your/config/dir") {
            @Override
            protected void handleAdd(File file) {
                // 实现新增配置逻辑
            }

            @Override
            protected void handleModify(File file) {
                // 实现更新配置逻辑
            }

            @Override
            protected void handleDelete(File file) {
                // 实现删除配置逻辑
            }
        };
        // 每5秒执行一次
        timer.scheduleAtFixedRate(watcher, 0, 5000);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 16:40:38