Java 挥发性与原子

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19744508/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 19:52:45  来源:igfitidea点击:

Volatile Vs Atomic

javavolatile

提问by Vaibhav

I read somewhere below line.

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

Java volatile keyword doesn't means atomic, its common misconception that after declaring volatile, ++operation will be atomic, to make the operation atomic you still need to ensure exclusive access using synchronizedmethod or block in Java.

Java volatile 关键字并不意味着原子性,它的普遍误解是声明了 volatile 后,++操作将是原子的,为了使操作具有原子性,您仍然需要确保synchronized在 Java 中使用方法或块进行独占访问 。

So what will happen if two threads attack a volatileprimitive variable at same time?

那么如果两个线程同时攻击一个volatile原始变量会发生什么?

Does this mean that whosoever takes lock on it, that will be setting its value first. And if in meantime, some other thread comes up and read old value while first thread was changing its value, then doesn't new thread will read its old value?

这是否意味着无论谁锁定它,都将首先设置其值。如果与此同时,在第一个线程更改其值时出现其他线程并读取旧值,那么新线程不会读取其旧值吗?

What is the difference between Atomic and volatile keyword?

Atomic 和 volatile 关键字有什么区别?

采纳答案by Louis Wasserman

The effect of the volatilekeyword is approximately that each individual read or write operation on that variable is atomic.

volatile关键字的效果大约是对该变量的每个单独的读或写操作都是原子的。

Notably, however, an operation that requires more than one read/write -- such as i++, which is equivalent to i = i + 1, which does one read and one write -- is notatomic, since another thread may write to ibetween the read and the write.

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

The Atomicclasses, like AtomicIntegerand AtomicReference, provide a wider variety of operations atomically, specifically including increment for AtomicInteger.

Atomic类,如AtomicIntegerAtomicReference,提供更广泛的各种操作的原子,具体包括增量AtomicInteger

回答by Trying

There are two important concepts in multithreading environment.

多线程环境中有两个重要的概念。

  1. atomicity
  2. visibility
  1. 原子性
  2. 能见度

Volatileeradicates visibility problem but it does not deal with atomicity. Volatilewill prevent compiler to reorder the instruction which involves write and subsequent read of a volatile variable. e.g. k++Here k++is not a single machine instruction rather it is three machine instructions.

Volatile根除可见性问题,但它不处理原子性。Volatile将阻止编译器重新排序涉及写入和随后读取易失性变量的指令。例如,k++这里k++不是单个机器指令,而是三个机器指令。

  1. copy the value to register
  2. increment it
  3. place it back
  1. 将值复制到寄存器
  2. 增加它
  3. 把它放回去

So even though you declare variable to volatileit will not make this operation atomic, which means another thread can see a intermediate result which is a stale or unwanted value for the other thread.

因此,即使您向volatile它声明变量也不会使此操作成为原子,这意味着另一个线程可以看到中间结果,该结果对于另一个线程来说是陈旧的或不需要的值。

But AtomicInteger, AtomicReferenceare based on the Compare and swap instruction. CAS has three operands: a memory location Von which to operate, the expected old value A, and the new value B. CASatomically updates Vto the new value B, but only if the value in Vmatches the expected old value A; otherwise it does nothing. In either case, it returns the value currently in V. This is used by JVM in AtomicInteger, AtomicReferenceand they call the function as compareAndSet(). If this functionality is not supported by underlying processor then JVM implements it by spin lock.

但是AtomicIntegerAtomicReference是基于比较和交换指令。CAS 具有三个操作数:要操作的内存位置V、预期的旧值A和新值BCAS自动更新V到新值B,但前提是 中的值V与预期的旧值匹配A;否则它什么都不做。在任何一种情况下,它都会返回当前在 中的值V。这由 JVM 在 中使用AtomicIntegerAtomicReference它们将函数称为compareAndSet(). 如果底层处理器不支持此功能,则 JVM 通过自旋锁实现它。

回答by scottb

As Trying as indicated, volatiledeals only with visibility.

正如所指示的那样,volatile仅处理可见性。

Consider this snippet in a concurrent environment:

在并发环境中考虑这个片段:

boolean isStopped = false;
    :
    :

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

The idea here is that some thread could change the value of isStoppedfrom false to true in order to indicate to the subsequent loop that it is time to stop looping.

这里的想法是某些线程可以将 的值isStopped从 false更改为 true,以便向后续循环指示是时候停止循环了。

Intuitively, there is no problem. Logically if another thread makes isStoppedequal to true, then the loop must terminate. The reality is that the loop will likely never terminate even if another thread makes isStoppedequal to true.

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

The reason for this is not intuitive, but consider that modern processors have multiple cores and that each core has multiple registers and multiple levels of cache memory that are not accessible to other processors. In other words, values that are cached in one processor's local memory are not visisbleto threads executing on a different processor. Herein lies one of the central problems with concurrency: visibility.

这样做的原因并不直观,但考虑到现代处理器有多个内核,每个内核都有多个寄存器和多级缓存,其他处理器无法访问。换句话说,缓存在一个处理器的本地内存中的对于在不同处理器上执行的线程是不可见的。这就是并发的核心问题之一:可见性。

The Java Memory Modelmakes no guarantees whatsoever about when changes that are made to a variable in one thread may become visible to other threads. In order to guarantee that updates are visisble as soon as they are made, you must synchronize.

Java内存模型使有关时被一个变量做在一个线程中的变化可能将对其他线程可见没有任何保证。为了保证更新一经完成就可见,您必须同步。

The volatilekeyword is a weak form of synchronization. While it does nothing for mutual exclusion or atomicity, it does provide a guarantee that changes made to a variable in one thread will become visible to other threads as soon as it is made. Because individual reads and writes to variables that are not 8-bytes are atomic in Java, declaring variables volatileprovides an easy mechanism for providing visibility in situations where there are no other atomicity or mutual exclusion requirements.

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

回答by Peter Lawrey

So what will happen if two threads attack a volatile primitive variable at same time?

那么如果两个线程同时攻击一个 volatile 原始变量会发生什么?

Usually each one can increment the value. However sometime, both will update the value at the same time and instead of incrementing by 2 total, both thread increment by 1 and only 1 is added.

通常每个人都可以增加该值。然而,有时,两者都会同时更新值,而不是总共增加 2,两个线程都增加 1 并且只添加 1。

Does this mean that whosoever takes lock on it, that will be setting its value first.

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

There is no lock. That is what synchronizedis for.

没有锁。那synchronized是为了什么。

And in if meantime, some other thread comes up and read old value while first thread was changing its value, then doesn't new thread will read its old value?

同时,如果其他线程在第一个线程更改其值时出现并读取旧值,那么新线程不会读取其旧值吗?

Yes,

是的,

What is the difference between Atomic and volatile keyword?

Atomic 和 volatile 关键字有什么区别?

AtomicXxxx wraps a volatile so they are basically same, the difference is that it provides higher level operations such as CompareAndSwap which is used to implement increment.

AtomicXxxx 封装了一个 volatile 所以它们基本相同,不同之处在于它提供了更高级别的操作,例如用于实现增量的 CompareAndSwap。

AtomicXxxx also supports lazySet. This is like a volatile set, but doesn't stall the pipeline waiting for the write to complete. It can mean that if you read a value you just write you might see the old value, but you shouldn't be doing that anyway. The difference is that setting a volatile takes about 5 ns, bit lazySet takes about 0.5 ns.

AtomicXxxx 也支持lazySet。这就像一个可变集,但不会停止等待写入完成的管道。这可能意味着,如果您读取您刚刚写入的值,您可能会看到旧值,但无论如何您都不应该这样做。不同的是,设置一个 volatile 大约需要 5 ns,bit lazySet 大约需要 0.5 ns。

回答by TwoThe

Volatile and Atomic are two different concepts. Volatile ensures, that a certain, expected (memory) state is true across different threads, while Atomics ensure that operation on variables are performed atomically.

易失性和原子性是两个不同的概念。Volatile 确保特定的预期(内存)状态跨不同线程为真,而 Atomics 确保对变量的操作以原子方式执行。

Take the following example of two threads in Java:

以Java中的两个线程为例:

Thread A:

线程 A:

value = 1;
done = true;

Thread B:

线程 B:

if (done)
  System.out.println(value);

Starting with value = 0and done = falsethe rule of threading tells us, that it is undefined whether or not Thread B will print value. Furthermore valueis undefined at that point as well!To explain this you need to know a bit about Java memory management (which can be complex), in short: Threads may create local copies of variables, and the JVM can reorder code to optimize it, therefore there is no guarantee that the above code is run in exactly that order. Setting done to true and thensetting value to 1 could be a possible outcome of the JIT optimizations.

与启动value = 0done = false线程的规律告诉我们,它是未定义与否线程B将打印值。此外,此时的也未定义!要解释这一点,您需要了解一些 Java 内存管理(可能很复杂),简而言之:线程可能会创建变量的本地副本,JVM 可以重新排序代码以对其进行优化,因此无法保证上述代码正是按照这个顺序运行的。将 done 设置为 true然后将 value 设置为 1 可能是 JIT 优化的可能结果。

volatileonly ensures, that at the moment of access of such a variable, the new value will be immediately visible to all other threads andthe order of execution ensures, that the code is at the state you would expect it to be. So in case of the code above, defining doneas volatilewill ensure that whenever Thread B checks the variable, it is either false, or true, and if it is true, then valuehas been set to 1 as well.

volatile仅确保在访问此类变量时,新值将立即对所有其他线程可见,并且执行顺序可确保代码处于您期望的状态。因此,对于上面的代码,定义donevolatile将确保每当线程 B 检查变量时,它要么是 false,要么是 true,如果是 true,则value也已设置为 1。

As a side-effect of volatile, the value of such a variable is set thread-wide atomically (at a very minor cost of execution speed). This is however only important on 32-bit systems that i.E. use long (64-bit) variables (or similar), in most other cases setting/reading a variable is atomic anyways. But there is an important difference between an atomic access and an atomic operation. Volatile only ensures that the access is atomically, while Atomics ensure that the operationis atomically.

作为volatile的副作用,此类变量的值在线程范围内以原子方式设置(以非常小的执行速度成本)。然而,这仅在 iE 使用长(64 位)变量(或类似变量)的 32 位系统上很重要,在大多数其他情况下,设置/读取变量无论如何都是原子的。但是原子访问和原子操作之间有一个重要的区别。Volatile 只保证访问是原子的,而 Atomics 保证操作是原子的。

Take the following example:

以下面的例子为例:

i = i + 1;

No matter how you define i, a different Thread reading the value just when the above line is executed might get i, or i + 1, because the operationis not atomically. If the other thread sets i to a different value, in worst case i could be set back to whatever it was before by thread A, because it was just in the middle of calculating i + 1 based on the old value, and then set i again to that old value + 1. Explanation:

不管你如何定义 i,一个不同的线程在执行上面的行时读取值可能会得到 i 或 i + 1,因为该操作不是原子的。如果另一个线程将 i 设置为不同的值,在最坏的情况下,线程 A 可以将 i 设置回之前的任何值,因为它只是在根据旧值计算 i + 1 的过程中,然后设置 i再次到旧值 + 1。 说明:

Assume i = 0
Thread A reads i, calculates i+1, which is 1
Thread B sets i to 1000 and returns
Thread A now sets i to the result of the operation, which is i = 1

Atomics like AtomicInteger ensure, that such operations happen atomically. So the above issue cannot happen, i would either be 1000 or 1001 once both threads are finished.

像 AtomicInteger 这样的原子确保这样的操作以原子方式发生。所以上述问题不会发生,一旦两个线程都完成,我要么是 1000 要么是 1001。

回答by Ortwin Angermeier

The volatilekeyword is used:

使用volatile关键字:

  • to make non atomic 64-bit operations atomic: longand double. (all other, primitive accesses are already guaranteed to be atomic!)
  • to make variable updates guaranteed to be seen by other threads + visibility effects: after writing to a volatile variable, all the variables that where visible before writing that variable become visible to another thread after reading the same volatile variable (happen-before ordering).
  • 使非原子 64 位操作原子化:longdouble。(所有其他原始访问已经保证是原子的!)
  • 使变量更新保证被其他线程看到 + 可见性效果:在写入 volatile 变量后,在写入该变量之前可见的所有变量在读取相同的 volatile 变量后对另一个线程可见(发生在排序之前)。

The java.util.concurrent.atomic.*classes are, according to the java docs:

java.util.concurrent.atomic.*根据java docs,这些类是:

A small toolkit of classes that support lock-free thread-safe programming on single variables. In essence, the classes in this package extend the notion of volatile values, fields, and array elements to those that also provide an atomic conditional update operation of the form:

boolean compareAndSet(expectedValue, updateValue);

支持对单个变量进行无锁线程安全编程的小型类工具包。本质上,这个包中的类将 volatile 值、字段和数组元素的概念扩展到那些还提供以下形式的原子条件更新操作的类:

boolean compareAndSet(expectedValue, updateValue);

The atomic classes are built around the atomic compareAndSet(...)function that maps to an atomic CPU instruction. The atomic classes introduce the happen-beforeordering as the volatilevariables do. (with one exception: weakCompareAndSet(...)).

原子类围绕compareAndSet(...)映射到原子 CPU 指令的原子函数构建。原子类和变量一样引入了先发生后排序volatile。(有一个例外:)weakCompareAndSet(...)

From the java docs:

从java文档:

When a thread sees an update to an atomic variable caused by a weakCompareAndSet, it does not necessarily see updates to any other variables that occurred before the weakCompareAndSet.

当线程看到由weakCompareAndSet 引起的原子变量更新时,它不一定会看到在weakCompareAndSet 之前发生的任何其他变量的更新。

To your question:

对于你的问题:

Does this mean that whosoever takes lock on it, that will be setting its value first. And in if meantime, some other thread comes up and read old value while first thread was changing its value, then doesn't new thread will read its old value?

这是否意味着无论谁锁定它,都将首先设置其值。同时,如果其他线程在第一个线程更改其值时出现并读取旧值,那么新线程不会读取其旧值吗?

You don't lock anything, what you are describing is a typical race condition that will happen eventually if threads access shared data without proper synchronization. As already mentioned declaring a variable volatilein this case will only ensure that other threads will see the change of the variable (the value will not be cached in a register of some cache that is only seen by one thread).

你没有锁定任何东西,你所描述的是一个典型的竞争条件,如果线程在没有适当同步的情况下访问共享数据,最终会发生这种情况。正如已经提到的,volatile在这种情况下声明一个变量只会确保其他线程会看到变量的变化(该值不会缓存在某个缓存的寄存器中,只有一个线程才能看到)。

What is the difference between AtomicIntegerand volatile int?

AtomicInteger和 和有volatile int什么区别?

AtomicIntegerprovides atomic operations on an intwith proper synchronization (eg. incrementAndGet(), getAndAdd(...), ...), volatile intwill just ensure the visibility of the intto other threads.

AtomicInteger提供了一个原子操作int以适当的同步(例如incrementAndGet()getAndAdd(...)...),volatile int将只保证的知名度int给其他线程。