Java volatile关键字使用中的值不一致问题咨询
问题分析:为什么T1更新volatile变量后,T3却输出旧值?
你遇到的这个问题核心点在于控制台输出的顺序 ≠ 线程实际读取volatile变量的内存操作顺序,这是因为System.out.println()是一个同步方法,线程需要争夺锁才能输出,这会打乱原本的内存操作时序和输出时序的对应关系。
具体的执行过程拆解
我们来看你提到的那个矛盾的输出片段:
T1, Resource's value = 35 T3, Resource's value = 20
实际的执行顺序可能是这样的:
- T3先执行到读取
resource.i的操作,此时主内存里i的值还是20,所以T3把20读到了自己的工作内存(虽然volatile会强制读主内存,但此时主内存的值还没被T1更新)。 - T3接着尝试调用
System.out.println(),但此时T1已经先拿到了System.out对象的锁,所以T3进入等待状态。 - 与此同时,T1完成了
resource.i = 35的写操作(volatile保证这个写会立刻刷新到主内存),然后调用println()输出35,之后释放锁。 - 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
相关产品推荐
相关产品推荐

