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

Java volatile内存序及x86-64编译:线程局部变量取值可能性分析

线程局部变量b和a同时取0的可能性分析

给定以下Java程序,我们需要分析线程thread1的局部变量b取值为0,且线程thread2的局部变量a取值为0的情况是否可能发生:

public class Main {
    public int a;
    public volatile int b;

    public void thread1(){
        int b;
        a = 1;
        b = this.b;
    }

    public void thread2(){
        int a;
        b = 1;
        a = this.a;
    }

    public static void main(String[] args) throws Exception {
        Main m = new Main();
        while(true){
            m.a = 0;
            m.b = 0;
            Thread t1 = new Thread(() -> m.thread1());
            Thread t2 = new Thread(() -> m.thread2());
            t1.start();
            t2.start();
            t1.join();
            t2.join();
        }
    }
}

JMM层面分析

从Java内存模型(JMM)的角度来看,我们无法证明这种情况不会发生。JMM对普通变量和volatile变量的重排序规则有明确约束:volatile读操作仅会禁止后续的读写操作被重排序到它之前,但不会限制前面的普通写操作被重排序到它之后。因此thread1中a=1这个普通写操作,完全有可能被重排序到b=this.b这个volatile读操作之后执行。

x86-64架构编译代码验证

为进一步确认,我们查看该程序在x86-64架构下的汇编编译结果(省略无关代码和注释):

thread1的汇编代码:

0x00007fb030dca235: movl    $0x1,0xc(%rsi)    ;*putfield a
0x00007fb030dca23c: mov     0x10(%rsi),%esi   ;*getfield b

thread2的汇编代码:

0x00007fb030dcc1b4: mov     $0x1,%edi
0x00007fb030dcc1b9: mov     %edi,0x10(%rsi)
0x00007fb030dcc1bc: lock addl $0x0,0xffffffffffffffc0(%rsp) ;*putfield b 
0x00007fb030dcc1c2: mov     0xc(%rsi),%esi    ;*getfield a

从汇编代码可以看出:

  • volatile读操作(thread1中读取this.b)不需要额外的内存屏障指令;
  • volatile写操作(thread2中写入this.b)后会插入lock addl指令(等效于mfence,起到内存屏障作用),确保自身之前的写操作对其他线程可见,但不会限制之后的普通读操作提前执行。

关键问题在于,thread1中的普通写a=1和volatile读b=this.b,在编译阶段或CPU层面仍然可能被重排序:thread1可能先执行b=this.b(此时this.b仍为0),再执行a=1;thread2则可能先执行a=this.a(此时this.a仍为0),再执行b=1。

结论

thread1的局部变量b取值为0,同时thread2的局部变量a取值为0的情况是可能发生的。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 10:57:34