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

为何@SpringBootApplication能让变量在多线程环境下具备可见性?

为何@SpringBootApplication能让未加volatile的变量具备可见性?

我在测试volatile的工作机制时,编写了如下代码并保留了@SpringBootApplication注解未删除。结果主线程将test设为true后,ThreadA停止运行;但按照理论,未给test添加volatile修饰时,ThreadA应持续运行。请问为何@SpringBootApplication能像volatile一样让test变量具备可见性?

@SpringBootApplication
public class FeignDemoApplication {
    private static boolean test = false;
    public static void main(String[] args) throws InterruptedException {
        System.out.println("Thread-Main :" + test);
        Thread threadA = new Thread(() -> {
            while (!test) {
            }
            System.out.println(Thread.currentThread().getName() + " :" + test);
        }, "ThreadA");
        threadA.start();
        Thread.sleep(1000);
        test = true;
        System.out.println("main stop:" + test);
    }
}

带@SpringBootApplication时的运行结果

Thread-Main :false
main stop:true
ThreadA :true
Process finished with exit code 0

不带@SpringBootApplication时的运行结果

Thread-Main :false
main stop:true

程序持续运行

原因分析

这本质是JVM即时编译(JIT)优化策略差异导致的:

  • 无@SpringBootApplication时,程序是普通Java应用,JVM会对ThreadA中的while (!test)循环做激进优化。因为test未被volatile修饰,JIT判定该变量在循环内不会被其他线程修改,于是将test的值缓存到线程本地寄存器中,不再每次读取主内存的最新值。主线程修改test后,ThreadA无法感知到变化,因此陷入死循环。
  • 加@SpringBootApplication时,Spring Boot启动过程会触发大量类加载、初始化操作,同时创建多线程执行任务,这些行为会改变JVM的JIT编译逻辑:一方面,多线程操作可能间接触发内存屏障,促使线程本地缓存失效,ThreadA会重新读取主内存的test值;另一方面,应用复杂度提升后,JVM不会对这个简单循环进行激进的缓存优化,因此ThreadA能实时感知到test被修改为true,从而退出循环。

需要明确的是,这并不是@SpringBootApplication本身具备volatile的语义,只是Spring Boot启动流程带来的JVM行为变化,属于非确定性的巧合。绝对不能依赖这种方式保证线程安全,正确做法是使用volatile关键字、synchronized锁或者AtomicBoolean等线程安全工具类来确保变量可见性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 18:55:18