zoukankan      html  css  js  c++  java
  • volatile为什么没有原子性?

    一、具体的实现原理

    • 对volatile变量执行写操作时,会在写操作后加入一条store屏障指令
    • 对volatile变量执行读操作时,会在读操作前加入一条load屏障指令

    说人话:

    • 对volatile变量执行读操作时,都要强制的先从主内存读取最新的变量值到工作内存,然后再读工作内存中所存储的变量副本
    • 对volatile变量执行写操作时,又会强制的将工作内存中的刚刚改变的值写到主内存中去

    通过上边这样模式,每个线程拿到的volatile变量值都是最新的。

    注意:

    volatile无法实现原子性:

    private volatile int count = 0;

    假设现在有两条线程分别对count执行加1操作,那么期待的结果最后count==2,但是看下边的分析:

    假设有如下流程:

    1)线程a获取了count==0;

    2)线程b获取了count==0;

    3)线程b对count+1,之后写入主内存count==1;

    4)线程a对count+1,之后写入主内存count==1;

    结果count==1而非count==2,原因就是线程a获取count后,volatile不能实现原子性,这个时候b也能去操作count。

    想要实现原子性,使用synchronized去锁住增加方法,或者使用ReentrantLock去锁住增加代码;当然,以上场景使用AtomicInteger更好。

    二、为什么不保证原子性

    The Java programming language provides a second mechanism, volatile fields, that is more convenient than locking for some purposes. A field may be declared volatile, in which case the Java Memory Model ensures that all threads see a consistent value for the variable.

    意思就是说,如果一个变量加了volatile关键字,就会告诉编译器和JVM的内存模型:这个变量是对所有线程共享的、可见的,每次jvm都会读取最新写入的值并使其最新值在所有CPU可见。volatile似乎是有时候可以代替简单的锁,似乎加了volatile关键字就省掉了锁。但又说volatile不能保证原子性(Java程序员很熟悉这句话:volatile仅仅用来保证该变量对所有线程的可见性,但不保证原子性)。这不是互相矛盾吗?

    不要将volatile用在getAndOperate场合,仅仅set或者get的场景是适合volatile的

    不要将volatile用在getAndOperate场合(这种场合不原子,需要再加锁),仅仅set或者get的场景是适合volatile的。

    volatile没有原子性举例:AtomicInteger自增

    例如你让一个volatile的integer自增(i++),其实要分成3步:1)读取volatile变量值到local; 2)增加变量的值;3)把local的值写回,让其它的线程可见。这3步的jvm指令为:

    mov   
    0xc(%r10),%r8d
     ; Load
    inc   
     %r8d           ; Increment
    mov   
     %r8d,0xc(%r10)
     ; Store
    lock
     addl $0x0,(%rsp)
     ; StoreLoad Barrier

    最后一个StoreLoad为内存屏障。

    什么是内存屏障

    内存屏障(memory barrier)是一个CPU指令。基本上,它是这样一条指令: a) 确保一些特定操作执行的顺序; b) 影响一些数据的可见性(可能是某些指令执行后的结果)。编译器和CPU可以在保证输出结果一样的情况下对指令重排序,使性能得到优化。插入一个内存屏障,相当于告诉CPU和编译器先于这个命令的必须先执行,后于这个命令的必须后执行。内存屏障另一个作用是强制更新一次不同CPU的缓存。例如,一个写屏障会把这个屏障前写入的数据刷新到缓存,这样任何试图读取该数据的线程将得到最新值,而不用考虑到底是被哪个cpu核心或者哪颗CPU执行的。

    内存屏障(memory barrier)和volatile什么关系?上面的虚拟机指令里面有提到,如果你的字段是volatile,Java内存模型将在写操作后插入一个写屏障指令,在读操作前插入一个读屏障指令。这意味着如果你对一个volatile字段进行写操作,你必须知道:1、一旦你完成写入,任何访问这个字段的线程将会得到最新的值。2、在你写入前,会保证所有之前发生的事已经发生,并且任何更新过的数据值也是可见的,因为内存屏障会把之前的写入值都刷新到缓存。

    volatile为什么没有原子性?

    明白了内存屏障(memory barrier)这个CPU指令,回到前面的JVM指令:从Load到store到内存屏障,一共4步,其中最后一步jvm让这个最新的变量的值在所有线程可见,也就是最后一步让所有的CPU内核都获得了最新的值,但中间的几步(从Load到Store)是不安全的,中间如果其他的CPU修改了值将会丢失。

  • 相关阅读:
    IXmlSerializable With WCFData Transfer in Service Contracts
    Difference Between XmlSerialization and BinarySerialization
    Using XmlSerializer (using Attributes like XmlElement , XmlAttribute etc ) Data Transfer in Service Contracts
    Introducing XML Serialization
    Version Tolerant Serialization
    Which binding is bestWCF Bindings
    Data Transfer in Service Contracts
    DataContract KnownTypeData Transfer in Service Contracts
    Using the Message ClassData Transfer in Service Contracts
    DataContract POCO SupportData Transfer in Service Contracts
  • 原文地址:https://www.cnblogs.com/yifanSJ/p/9224341.html
Copyright © 2011-2022 走看看