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

Java懒加载与空检查:移除方法级synchronized后的代码是否可行?

修改后的代码完全不可行,还存在几个致命问题
  1. 直接触发空指针异常:你把wordsInFile的初始化代码去掉了,它一开始是null,执行synchronized(wordsInFile)时会直接抛出NullPointerException——Java里锁对象不能是null。就算你没删初始化,同步块里重新赋值wordsInFile = new HashSet<>()后,锁对象会被替换,后续线程的锁会失效,完全起不到同步加载的作用,大概率会出现多线程重复加载资源的情况。
  2. static方法访问非static成员的基础错误:不管是原始代码还是修改后的代码,IsFound是static方法,但wordsInFile是非static成员,这在Java里编译都通不过——static方法不能直接访问实例变量,必须先创建类的实例才行。
  3. 空集合判断的隐患:就算解决了null问题,用wordsInFile.isEmpty()判断是否需要加载也不靠谱,如果资源加载失败(比如文件找不到),wordsInFile会是空集合,后续每次调用都会重复尝试加载,白白浪费资源。

正确的优化方案(懒加载+减少锁范围)

要实现“只在加载时加锁,查询时无锁”的需求,应该用双重检查锁(DCL),同时保证变量的可见性,还要修正static/非static的匹配问题,示例代码如下:

// volatile保证多线程下的可见性,禁止指令重排
private static volatile Set<String> wordsInFile;

// 方法名遵循Java小驼峰规范
private static boolean isFound(String word) {
    if (word == null) {
        return false;
    }

    // 第一次无锁检查,避免每次调用都进入同步块
    if (wordsInFile == null) {
        // 用类对象当锁,匹配static方法的作用域
        synchronized (WebUtils.class) {
            // 第二次检查,防止多个线程等待锁后重复初始化
            if (wordsInFile == null) {
                // 先初始化临时集合,避免其他线程拿到未加载完成的集合
                Set<String> tempWordSet = new HashSet<>();
                try (InputStream inputStream = WebUtils.class.getClassLoader().getResourceAsStream("words.txt");
                     InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
                     BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
                    String line;
                    while ((line = bufferedReader.readLine()) != null) {
                        tempWordSet.add(line);
                    }
                    // 加载完成后再赋值给全局变量
                    wordsInFile = tempWordSet;
                } catch (Exception e) {
                    // 不要空吞异常,至少打日志方便排查
                    e.printStackTrace();
                    // 加载失败可以初始化空集合,避免后续重复尝试加载
                    wordsInFile = new HashSet<>();
                }
            }
        }
    }

    return wordsInFile.contains(word.toLowerCase());
}

关键优化点:

  • 用volatile修饰全局集合,防止多线程下出现“指令重排”导致的未初始化集合访问。
  • 双重检查锁:第一次无锁判断避免每次调用都进入同步块,第二次锁内判断防止重复初始化。
  • 先加载到临时集合再赋值,避免其他线程拿到未加载完成的集合。
  • 处理加载异常,避免无限重复加载失败的资源。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 11:12:23