volatile语句的负载障碍在哪里?

我写了这个简单的Java程序:

package com.salil.threads;

public class IncrementClass {

    static volatile int j = 0;
    static int i = 0;

    public static void main(String args[]) {

        for(int a=0;a<1000000;a++);
        i++;
        j++;            
    }       
}

这为i ++和j ++生成以下反汇编代码(其余反汇编代码已删除):

  0x0000000002961a6c: 49ba98e8d0d507000000 mov       r10,7d5d0e898h
                                                ;   {oop(a 'java/lang/Class' = 'com/salil/threads/IncrementClass')}
  0x0000000002961a76: 41ff4274            inc       dword ptr [r10+74h]
                                                ;*if_icmpge
                                                ; - com.salil.threads.IncrementClass::main@5 (line 10)
  0x0000000002961a7a: 458b5a70            mov       r11d,dword ptr [r10+70h]
  0x0000000002961a7e: 41ffc3              inc       r11d
  0x0000000002961a81: 45895a70            mov       dword ptr [r10+70h],r11d
  0x0000000002961a85: f083042400          lock add  dword ptr [rsp],0h
                                                ;*putstatic j
                                                ; - com.salil.threads.IncrementClass::main@27 (line 14)

这是我对以下汇编代码的理解:

  • mov r10,7d5d0e898h:将指针移动到IncrementClass.class以注册r10
  • inc dword ptr [r10 + 74h]:在地址[r10 + 74h]处(即i)增加4字节值,
  • mov r11d,dword ptr [r10 + 70h]:将地址[r10 + 70h]处的4值移到寄存器r11d(即移动j的值到r11d)
  • 公司r11d:递增r11d
  • mov dword ptr [r10 + 70h],r11d:将r11d的值写入[r10 + 70h],以便其他线程可见--lock add dword ptr [rsp],0h:锁定堆栈指针rsp所表示的内存地址,给它加0。
  • JMM指出,在每次易失性读取之前,必须有一个加载内存屏障,并且在每次易失性写入之后都必须存在存储屏障。 我的问题是:

  • 为什么在将j读入r11d之前没有负载障碍?
  • 锁定和添加到rsp如何确保r11d中j的值传回主内存。 我从intel规格中读到的是,锁在操作期间向CPU提供了对指定内存地址的独占锁定。

  • 英特尔处理器x86具有强大的内存模型。

    因此,所有的障碍StoreStore,LoadLoad,LoadStore在x86上都是no-op。 除了可以通过 mfence或cpuid或锁定insn 实现的 StoreLoad外。 您可以使用汇编代码进行确认。 其他障碍只意味着限制编译器的优化和转换,以便它们不会破坏Java内存模型规范。

    当你在英特尔处理器上运行时,我假设它的x86版本。

    请阅读

  • http://gee.cs.oswego.edu/dl/jmm/cookbook.html以供参考。

  • http://psy-lob-saw.blogspot.com/2013/08/memory-barriers-are-not-free.html

  • http://jsr166-concurrency.10961.n7.nabble.com/x86-NOOP-memory-barriers-td9991.html
  • 锁不是一条指令,而是更多的指令前缀(表现为storeLoad障碍)。

  • 在x86汇编中,“lock”指令意味着什么?
  • 为什么我们在CMPXCHG之前需要锁定前缀

  • Java中的volatile关键字只能保证线程本地副本和高速缓存将被跳过,并且该值将直接从主内存中加载或写入主内存。 但它不包含锁定机制。 因此,从volatile读取或写入volatile是原子性的,但需要执行一系列读写操作,如上所述

    j++

    不是原子,因为一些其他线程可以修改的值j读之间和写入主存储器中的变量。 要实现原子增量,您需要使用包含在Java中的Atomic类中的CAS操作,例如AtomicInteger等。或者,如果您更喜欢低级编程,则可以在Unsafe类中使用原子方法,例如Unsafe.compareAndSwapInt等。


    由于你的程序是单线程的(只有一个线程 - 主线程),所以你的JIT编译器可以优化屏障,就像单线程环境下的锁可以被优化一样。 此优化独立于处理器架构。

    链接地址: http://www.djcxy.com/p/67095.html

    上一篇: Where is the load barrier for the volatile statement?

    下一篇: converting C code into assembly