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

该静态内部类初始化方式是否存在多线程可见性问题?

初始化可见性问题结论

该写法不存在可见性问题,多线程并发调用handle()方法时,redisTemplate一定是已完成初始化的非null值,不会出现取值为null的场景。

核心逻辑依据

  • JVM对类初始化阶段有原生的线程安全与可见性保证:类初始化阶段执行的<clinit>()方法(包含静态变量赋值、静态代码块逻辑),虚拟机会自动加锁同步。多线程同时触发同一个类初始化时,仅会有一个线程执行类的<clinit>()逻辑,其余线程全部阻塞等待,直到初始化执行完毕。
  • 类初始化的同步规则天然满足JMM的happens-before约定:执行类初始化的线程在释放初始化锁前,会将所有操作的写入结果刷回主存;后续阻塞等待的线程被唤醒后,会从主存读取最新的共享变量值,保证能看到初始化阶段所有赋值操作的结果。

对应代码的执行逻辑

示例代码如下:

public class InnerStaticClassExample {
    private static RedisTemplate<String, Object> redisTemplate;

    private static class Initializer {
        static {
            redisTemplate = new RedisTemplate<>();
        }

        private static void init(){
        }
    }

    /**
     * Multithread concurrent call
     */
    public static void handle(String key, long time, TimeUnit timeUnit) {
        Initializer.init();
        redisTemplate.expire(key, time, timeUnit);
    }
}
  • 当任意线程首次调用Initializer.init()时,会首次触发Initializer类的初始化,拿到初始化锁的线程会执行静态代码块,完成redisTemplate的实例化与赋值。
  • 静态代码块执行完成后Initializer类才算初始化完成,此时等待初始化锁的其他线程才会被唤醒继续执行,所有线程都能读取到redisTemplate已完成赋值的状态。
  • 类初始化只会执行一次,后续所有线程调用Initializer.init()时,Initializer已经完成初始化,不会重复执行静态代码块,直接进入后续逻辑,不存在重复赋值或者读到未赋值状态的可能。

常见误区说明

有观点认为redisTemplate字段没有加volatile修饰,会出现指令重排序导致读到未完成初始化的对象,这个判断不适用于当前场景:

  • 普通懒汉式单例在无锁、无volatile修饰下的问题,是因为没有公共同步点兜底重排序与可见性;而当前场景下的赋值动作被类初始化的锁机制完全包裹,初始化完成的同步点已经保证了所有写入结果对后续线程可见,不需要额外加volatile修饰就能保证线程安全。
  • 这本质上是经典静态内部类单例模式的变体,初始化安全性由JVM层面兜底。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 13:15:41