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

Java volatile关键字使用中的值不一致问题咨询

问题分析:为什么T1更新volatile变量后,T3却输出旧值?

你遇到的这个问题核心点在于控制台输出的顺序 ≠ 线程实际读取volatile变量的内存操作顺序,这是因为System.out.println()是一个同步方法,线程需要争夺锁才能输出,这会打乱原本的内存操作时序和输出时序的对应关系。

具体的执行过程拆解

我们来看你提到的那个矛盾的输出片段:

T1, Resource's value = 35
T3, Resource's value = 20

实际的执行顺序可能是这样的:

  1. T3先执行到读取resource.i的操作,此时主内存里i的值还是20,所以T3把20读到了自己的工作内存(虽然volatile会强制读主内存,但此时主内存的值还没被T1更新)。
  2. T3接着尝试调用System.out.println(),但此时T1已经先拿到了System.out对象的锁,所以T3进入等待状态。
  3. 与此同时,T1完成了resource.i = 35的写操作(volatile保证这个写会立刻刷新到主内存),然后调用println()输出35,之后释放锁。
  4. T3终于拿到锁,把之前读取到的20输出到控制台。

这就导致了控制台输出顺序看起来是T1先更新了值,但T3却读到了旧值,但实际上T3读取旧值的操作发生在T1更新之前,只是输出被锁阻塞滞后了。

关于volatile的认知补全

你提到自己执行的是原子操作,这点没错:对int类型的volatile变量的写和读都是原子操作。但volatile只保证:

  • 写操作完成后,后续所有对该变量的读操作都能看到最新值(这里的“后续”是指内存操作的时间顺序,不是控制台输出的顺序)。
  • 禁止指令重排序,保证写操作和之前的代码、读操作和之后的代码不会被打乱。

但它管不了线程调度的顺序,也管不了像println()这种同步操作带来的输出滞后问题。

验证解决方法

如果想让输出顺序严格反映内存操作的顺序,可以把读取变量和打印操作放在同一个同步块里,比如修改T2、T3的run方法:

@Override
public void run() {
    int count = 1;
    while(count <= 1000) {
        if ("T1".equals(name)) {
            if (count % 5 == 0) {
                synchronized (resource) {
                    resource.i = count;
                    System.out.println(name + ", Resource's value = " + resource.i);
                }
            }
        } else {
            synchronized (resource) {
                System.out.println(name + ", Resource's value = " + resource.i);
            }
        }
        count++;
    }
}

这样一来,读取i和打印的操作变成了一个原子性的同步块,线程必须先获取锁才能读取和打印,输出顺序就会和内存操作的顺序一致了——你不会再看到T1更新值之后,其他线程输出旧值的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 23:57:37