何时在 Java 中使用 AtomicReference?
When do we use AtomicReference
?
Is it needed to create objects in all multithreaded programs?
Provide a simple example where AtomicReference should be used.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
原子引用应该用在需要执行简单原子的设置中(即<对引用的线程安全(em>线程安全,非平凡)操作,基于监视器的同步对此不合适。假设您只想在处理过程中对象状态发生更改时设置特定字段:
由于原子引用语义,即使
cache
对象在线程之间共享,您也可以执行此操作,而无需使用已同步
。一般来说,除非您知道自己在做什么,否则最好使用同步器或java.util.concurrent
框架,而不是裸露的Atomic*
。两篇优秀的死树参考资料将向您介绍这个主题:
请注意(我不知道这是否一直如此)引用赋值(即
=
)本身就是原子的(更新原始 64位类型,例如long
或double
可能不是原子的;但更新引用始终是原子的,即使它是 64 位)而不显式使用原子*
。请参阅 Java 语言规范 3ed,第 17.7 节。
Atomic reference should be used in a setting where you need to do simple atomic (i.e. thread-safe, non-trivial) operations on a reference, for which monitor-based synchronization is not appropriate. Suppose you want to set a specific field only if the state of the object has changed during processing:
Because of the atomic reference semantics, you can do this even if the
cache
object is shared amongst threads, without usingsynchronized
. In general, you're better off using synchronizers or thejava.util.concurrent
framework rather than bareAtomic*
unless you know what you're doing.Two excellent dead-tree references which will introduce you to this topic:
Note that (I don't know if this has always been true) reference assignment (i.e.
=
) is itself atomic (updating primitive 64-bit types likelong
ordouble
may not be atomic; but updating a reference is always atomic, even if it's 64 bit) without explicitly using anAtomic*
.See the Java Language Specification 3ed, Section 17.7.
当您需要通过将多个线程访问的内容(在不可变对象中)替换为这些线程之间共享的新副本(不可变对象)来更新它时,原子引用是理想的选择。这是一个非常密集的陈述,所以我将对其进行一些分解。
首先,不可变对象是在构造后实际上不会改变的对象。通常,不可变对象的方法会返回同一类的新实例。一些示例包括包装类
Long
和Double
,以及String
,仅举几例。 (根据 JVM 上的编程并发,不可变对象是现代并发的关键部分。)接下来,为什么
AtomicReference
比易失性
更好> 共享该共享价值的对象?一个简单的代码示例将显示差异。每次想要根据当前值修改该易失性字段引用的字符串时,首先需要获得该对象的锁。这可以防止其他线程在此期间进入并更改新字符串连接中间的值。然后,当您的线程恢复时,您会破坏另一个线程的工作。但老实说,该代码可以工作,看起来很干净,并且会让大多数人感到高兴。
有点问题。它很慢。特别是当该锁对象存在大量争用时。那是因为大多数锁都需要操作系统系统调用,并且您的线程将被阻塞并被上下文切换出 CPU,以便为其他进程让路。
另一种选择是使用 AtomicReference。
现在为什么这样更好呢?老实说,代码比以前稍微不那么干净了。但在 AtomicRefrence 的幕后发生了一些非常重要的事情,那就是比较和交换。
导致切换发生的是单个 CPU 指令,而不是操作系统调用。这是CPU 上的一条指令。而且因为没有锁,所以在行使锁的情况下没有上下文切换,这节省了更多时间!
问题是,对于 AtomicReferences,这不使用
.equals()
调用,而是使用==
比较预期值。因此,请确保预期的是循环中 get 返回的实际对象。An atomic reference is ideal to use when you need to update content (in an immutable object) accessed by multiple threads by replacing it with a new copy (of the immutable object) shared between these threads. That is a super dense statement, so I will break it down a bit.
First, an immutable object is an object that is effectively not changed after construction. Frequently, an immutable object's methods return new instances of that same class. Some examples include the wrapper classes
Long
andDouble
, as well asString
, just to name a few. (According to Programming Concurrency on the JVM, immutable objects are a critical part of modern concurrency.)Next, why is an
AtomicReference
better than avolatile
object for sharing that shared value? A simple code example will show the difference.Every time you want to modify the string referenced by that volatile field based on its current value, you first need to obtain a lock on that object. This prevents some other thread from coming in during the meantime and changing the value in the middle of the new string concatenation. Then when your thread resumes, you clobber the work of the other thread. But honestly that code will work, it looks clean, and it would make most people happy.
Slight problem. It is slow. Especially if there is a lot of contention of that lock Object. Thats because most locks require an OS system call, and your thread will block and be context switched out of the CPU to make way for other processes.
The other option is to use an AtomicReference.
Now why is this better? Honestly that code is a little less clean than before. But there is something really important that happens under the hood in AtomicRefrence, and that is compare and swap.
It is a single CPU instruction, not an OS call, that makes the switch happen. That is a single instruction on the CPU. And because there are no locks, there is no context switch in the case where the lock gets exercised which saves even more time!
The catch is, for AtomicReferences, this does not use a
.equals()
call, but instead an==
comparison for the expected value. So make sure the expected is the actual object returned from get in the loop.以下是 AtomicReference 的一个用例:
考虑充当数字范围的此类,并使用单独的 AtmomicInteger 变量来维护数字下限和上限。
setLower 和 setUpper 都是先检查后执行的序列,但它们没有使用足够的锁定来使它们成为原子的。如果数字范围为 (0, 10),并且一个线程调用 setLower(5),而另一个线程调用 setUpper(4),那么在某些不幸的时机,两个线程都将通过 setter 中的检查,并且两个修改都将被应用。结果是范围现在保持 (5, 4) 无效状态。因此,虽然底层 AtomicIntegers 是线程安全的,但复合类却不是。这可以通过使用 AtomicReference 而不是使用单独的 AtomicIntegers 作为上限和下限来解决。
Here is a use case for AtomicReference:
Consider this class that acts as a number range, and uses individual AtmomicInteger variables to maintain lower and upper number bounds.
Both setLower and setUpper are check-then-act sequences, but they do not use sufficient locking to make them atomic. If the number range holds (0, 10), and one thread calls setLower(5) while another thread calls setUpper(4), with some unlucky timing both will pass the checks in the setters and both modifications will be applied. The result is that the range now holds (5, 4)an invalid state. So while the underlying AtomicIntegers are thread-safe, the composite class is not. This can be fixed by using a AtomicReference instead of using individual AtomicIntegers for upper and lower bounds.
应用乐观锁时可以使用 AtomicReference。您有一个共享对象,并且想要从多个线程更改它。
因为其他线程可能已经修改了它并且/可以在这两个步骤之间进行修改。您需要在原子操作中完成它。这就是 AtomicReference 可以提供帮助的地方
You can use AtomicReference when applying optimistic locks. You have a shared object and you want to change it from more than 1 thread.
As other thread might have modified it and/can modify between these 2 steps. You need to do it in an atomic operation. this is where AtomicReference can help
这是一个非常简单的用例,与线程安全无关。
要在 lambda 调用之间共享对象,
AtomicReference
是一个选项:我并不是说这是好的设计或任何东西(这只是一个简单的示例),但是如果您在某些情况下,您需要在 lambda 调用之间共享对象,
AtomicReference
是一个选项。事实上,您可以使用任何持有引用的对象,甚至是只有一项的 Collection。然而,AtomicReference 是一个完美的选择。
Here's a very simple use case and has nothing to do with thread safety.
To share an object between lambda invocations, the
AtomicReference
is an option:I'm not saying this is good design or anything (it's just a trivial example), but if you have have the case where you need to share an object between lambda invocations, the
AtomicReference
is an option.In fact you can use any object that holds a reference, even a Collection that has only one item. However, the AtomicReference is a perfect fit.
我们什么时候使用 AtomicReference?
有多种方法可以实现线程安全并发 API。原子变量就是其中之一。
提供一个应使用 AtomicReference 的简单示例。
使用
AtomicReference
的示例代码:是否需要在所有多线程程序中创建对象?
您不需要不必在所有多线程程序中使用
AtomicReference
。如果您想保护单个变量,请使用
AtomicReference
。如果您想保护代码块,请使用其他构造,例如Lock
/synchronized
等。来源:docs.oracle.com
When do we use AtomicReference?
There are multiple ways of achieving Thread safety concurrent API. Atomic variables is one of them.
Provide a simple example where AtomicReference should be used.
Sample code with
AtomicReference
:Is it needed to create objects in all multithreaded programs?
You don't have to use
AtomicReference
in all multi threaded programs.If you want to guard a single variable, use
AtomicReference
. If you want to guard a code block, use other constructs likeLock
/synchronized
etc.Source: docs.oracle.com
我就不多说了。我尊敬的朋友们已经提供了宝贵的意见。 本博客最后的完整运行代码应该可以消除任何混乱。这是一个多线程场景下的电影订座小程序。
一些重要的基本事实如下。
1>不同的线程只能竞争堆空间中的实例和静态成员变量。
2>易失性读取或写入完全是原子的,并且序列化/发生在内存之前并且仅从内存中完成。我这么说的意思是任何读取都将遵循内存中的先前写入。任何写入都将遵循先前从内存中读取的操作。因此任何使用易失性的线程都将始终看到最新的值。
AtomicReference 使用了 volatile 的这个属性。
以下是 AtomicReference 的一些源代码。
AtomicReference 指的是对象引用。该引用是 AtomicReference 实例中的一个易失性成员变量,如下所示。
get() 只是返回变量的最新值(就像 volatile 以“发生在之前”的方式所做的那样)。
以下是AtomicReference最重要的方法。
compareAndSet(expect,update)方法调用Java的unsafe类的compareAndSwapObject()方法。 unsafe 的此方法调用会调用本机调用,该调用会调用处理器的单个指令。 “expect”和“update”各自引用一个对象。
当且仅当 AtomicReference 实例成员变量“value”引用“expect”引用的同一个对象时,现在将“update”赋值给该实例变量,并返回“true”。否则返回 false。整个事情都是原子完成的。没有其他线程可以在其间拦截。
由于这是单处理器操作(现代计算机体系结构的魔力),因此它通常比使用同步块更快。但是请记住,当需要原子更新多个变量时,AtomicReference 将无济于事。
我想添加一个完整的运行代码,它可以在 eclipse 中运行。它将消除许多困惑。这里有 22 个用户(MyTh 线程)正在尝试预订 20 个座位。以下是代码片段,后面是完整代码。
代码片段,其中 22 个用户尝试预订 20 个座位。
以下是完整的运行代码。
I won't talk much. Already my respected fellow friends have given their valuable input. The full fledged running code at the last of this blog should remove any confusion. It's about a movie seat booking small program in multi-threaded scenario.
Some important elementary facts are as follows.
1> Different threads can only contend for instance and static member variables in the heap space.
2> Volatile read or write are completely atomic and serialized/happens before and only done from memory. By saying this I mean that any read will follow the previous write in memory. And any write will follow the previous read from memory. So any thread working with a volatile will always see the most up-to-date value.
AtomicReference uses this property of volatile.
Following are some of the source code of AtomicReference.
AtomicReference refers to an object reference. This reference is a volatile member variable in the AtomicReference instance as below.
get() simply returns the latest value of the variable (as volatiles do in a "happens before" manner).
Following is the most important method of AtomicReference.
The compareAndSet(expect,update) method calls the compareAndSwapObject() method of the unsafe class of Java. This method call of unsafe invokes the native call, which invokes a single instruction to the processor. "expect" and "update" each reference an object.
If and only if the AtomicReference instance member variable "value" refers to the same object is referred to by "expect", "update" is assigned to this instance variable now, and "true" is returned. Or else, false is returned. The whole thing is done atomically. No other thread can intercept in between.
As this is a single processor operation (magic of modern computer architecture), it's often faster than using a synchronized block. But remember that when multiple variables need to be updated atomically, AtomicReference won't help.
I would like to add a full fledged running code, which can be run in eclipse. It would clear many confusion. Here 22 users (MyTh threads) are trying to book 20 seats. Following is the code snippet followed by the full code.
Code snippet where 22 users are trying to book 20 seats.
Following is the full running code.