Java 挥发性与原子性

Java 挥发性与原子性,java,volatile,Java,Volatile,我在下面的某个地方读到了 Java volatile关键字并不意味着原子,这是它常见的误解 声明volatile后,++操作将是原子的,以使 您仍然需要使用原子操作来确保独占访问 synchronizedJava中的方法或块 那么,如果两个线程同时攻击volatile基本变量,会发生什么情况呢 这是否意味着无论谁锁定它,都将首先设置它的值。若同时,当第一个线程改变它的值时,另一个线程出现并读取旧值,那个么新线程不会读取它的旧值吗 原子关键字和volatile关键字之间的区别是什么?volatil

我在下面的某个地方读到了

Java volatile关键字并不意味着原子,这是它常见的误解 声明volatile后,
++
操作将是原子的,以使 您仍然需要使用原子操作来确保独占访问
synchronized
Java中的方法或块

那么,如果两个线程同时攻击
volatile
基本变量,会发生什么情况呢

这是否意味着无论谁锁定它,都将首先设置它的值。若同时,当第一个线程改变它的值时,另一个线程出现并读取旧值,那个么新线程不会读取它的旧值吗


原子关键字和volatile关键字之间的区别是什么?

volatile关键字的作用大约是使该变量上的每个单独读或写操作对所有线程原子可见

然而,值得注意的是,一个需要多次读/写的操作——例如
i++
,相当于执行一次读和一次写的
i=i+1
——不是原子操作,因为另一个线程可能会在读和写之间写入
i


AtomicInteger
AtomicReference
AtomicInteger类提供了更广泛的原子操作,具体包括
AtomicInteger

的增量在多线程环境中有两个重要概念:

boolean isStopped = false;
    :
    :

    while (!isStopped) {
        // do some kind of work
    }

  • volatile关键字消除了可见性问题,但它不处理原子性
    volatile
    将防止编译器重新排序指令,这些指令涉及对volatile变量的写入和后续读取;e、 g.
    k++
    。 这里,
    k++
    不是一条机器指令,而是三条:

  • 将值复制到寄存器中
  • 增值
  • 把它放回去
  • 因此,即使将变量声明为volatile,这也不会使该操作原子化;这意味着另一个线程可以看到一个中间结果,它是另一个线程的过时或不需要的值


    另一方面,它们都是基于。CAS有三个操作数:要操作的内存位置
    V
    、预期的旧值
    a
    、新值
    B
    CAS
    自动将
    V
    更新为新值
    B
    ,但前提是
    V
    中的值与预期的旧值
    A
    匹配;否则,它将一事无成。在这两种情况下,它都返回当前在
    V
    中的值。
    AtomicInteger
    AtomicReference
    compareAndSet()
    方法利用此功能(如果底层处理器支持);如果不是,那么JVM通过实现它。

    正如所示,
    volatile
    只处理可见性

    在并发环境中考虑此代码段:

    boolean isStopped = false;
        :
        :
    
        while (!isStopped) {
            // do some kind of work
        }
    
    这里的想法是,一些线程可以将
    isStopped
    的值从false更改为true,以便向后续循环指示是时候停止循环了

    凭直觉,这没有问题。从逻辑上讲,如果另一个线程使
    isStopped
    等于true,则循环必须终止。事实是,即使另一个线程使
    isStopped
    等于true,循环也可能永远不会终止

    这是不直观的,但是考虑到现代处理器有多个内核,每个内核都有多个寄存器和多个高速缓冲存储器,<>强>不能被其他处理器< /强>访问。换句话说,缓存在一个处理器的本地内存中的值对于在不同处理器上执行的线程来说是不可访问的。这里存在着并发的一个核心问题:可见性

    对于对一个线程中的变量所做的更改何时会对其他线程可见,不作任何保证。为了保证更新一经进行就可以访问,您必须同步


    volatile
    关键字是一种弱同步形式。虽然它对互斥性或原子性没有任何作用,但它确实提供了一种保证,即对一个线程中的变量所做的更改一经做出,其他线程就会看到。因为对非8字节变量的单独读写在Java中是原子的,所以声明变量
    volatile
    提供了一种简单的机制,可以在没有其他原子性或互斥要求的情况下提供可见性

    那么,如果两个线程同时攻击一个易失性原语变量,会发生什么呢

    通常每一个都可以增加值。但是,有时,两个线程都会同时更新该值,而不是总共增加2个线程,而是增加1个线程,只增加1个线程

    这是否意味着无论谁锁定它,都将首先设置它的值

    没有锁。这就是
    synchronized
    的用途

    若同时,当第一个线程改变它的值时,另一个线程出现并读取旧值,那个么新线程不会读取它的旧值吗


    原子关键字和volatile关键字之间的区别是什么

    AtomicXxxx包装了一个volatile,所以它们基本上是相同的,区别在于它提供了更高级别的操作,例如用于实现增量的CompareAndSwap

    AtomicXxxx还支持lazySet。这就像一个易失性集,但不会暂停管道等待写入完成。这可能意味着,如果您读取一个值,您只需编写它,您可能会看到旧值,但无论如何,您不应该这样做。不同之处在于设置一个volatile大约需要5ns,位懒散大约需要0.5ns。

    volatile和
    if (done)
      System.out.println(value);
    
    i = i + 1;