原子引用应该在需要对引用执行简单原子(即线程安全、非平凡)操作的设置中使用,对于这种操作,基于监视器的同步是不合适的。假设仅当对象的状态在处理过程中发生更改时才要设置特定字段:
AtomicReference<Object> cache = new AtomicReference<Object>();
Object cachedValue = new Object();
cache.set(cachedValue);
//... time passes ...
Object cachedValueToUpdate = cache.get();
//... do some work to transform cachedValueToUpdate into a new version
Object newValue = someFunctionOfOld(cachedValueToUpdate);
boolean success = cache.compareAndSet(cachedValue,cachedValueToUpdate);
由于原子引用语义,即使对象在线程之间共享,也可以执行此操作,而无需使用 。一般来说,你最好使用同步器或框架,而不是裸露,除非你知道你在做什么。cache
synchronized
java.util.concurrent
Atomic*
两个优秀的死树参考,将向您介绍这个主题:
请注意(我不知道这是否一直都是真的)引用赋值(即)本身是原子的(更新原始的64位类型,如或可能不是原子的;但更新引用始终是原子的,即使它是64位),而没有显式使用。
请参阅 Java 语言规范 3ed, 第 17.7 节。=
long
double
Atomic*
当您需要在多个线程之间共享和更改不可变对象的状态时,非常适合使用原子引用。这是一个超级密集的声明,所以我将稍微分解一下。
首先,不可变对象是构造后实际上不会更改的对象。通常,不可变对象的方法返回同一类的新实例。一些例子包括包装类和,以及,仅举几例。(根据JVM上的编程并发,不可变对象是现代并发的关键部分。Long
Double
String
接下来,为什么分享这种共享价值比对象更好?一个简单的代码示例将显示差异。AtomicReference
volatile
volatile String sharedValue;
static final Object lock = new Object();
void modifyString() {
synchronized (lock) {
sharedValue = sharedValue + "something to add";
}
}
每次要根据该易失性字段的当前值修改其引用的字符串时,首先需要获取该对象的锁定。这可以防止其他线程在此期间进入并更改新字符串串联中间的值。然后,当您的线程恢复时,您将删除其他线程的工作。但老实说,这些代码会起作用,它看起来很干净,它会让大多数人开心。
轻微的问题。它很慢。特别是如果该锁对象存在大量争用。这是因为大多数锁都需要操作系统系统调用,并且您的线程将阻塞并将上下文从CPU中切换出来,以便为其他进程让路。
另一个选项是使用原子折射。
public static AtomicReference<String> shared = new AtomicReference<>();
String init = "Inital Value";
shared.set(init);
//now we will modify that value
boolean success = false;
while (!success) {
String prevValue = shared.get();
// do all the work you need to
String newValue = shared.get() + "let's add something";
// Compare and set
success = shared.compareAndSet(prevValue, newValue);
}
为什么这样更好呢?老实说,该代码比以前更不干净。但是在AtomicRefrence中,有一些非常重要的事情发生在引擎盖下,那就是比较和交换。它是单个CPU指令,而不是操作系统调用,使切换发生。这是 CPU 上的单个指令。而且由于没有锁,因此在执行锁的情况下没有上下文切换,从而节省了更多时间!
问题是,对于 AtomicReferences,这不使用调用,而是使用预期值的比较。因此,请确保预期的是从 get in 循环返回的实际对象。.equals()
==
-
-
如何检测我的方法在等待什么? 我在Java中有一个调用其他几个方法的方法。此方法是从固定线程池中的多个线程调用的。工作线程数与可用处理器(内核)数相同。 当我使用 VisualVM 分析程序执行时,时间和时间非常短,但
-
Java 服务器的单线程池还是多线程池?[已关闭] 我正在编写一个相当复杂的Java服务器应用程序,除了通常的请求 - 响应处理之外,它还具有重要的后台处理部分。一些后台处理是使用Quartz框架以类似cron的方式完成的。
-
-
从直方图计算平均值和百分位数? 我编写了一个计时器,它将测量任何多线程应用程序中特定代码的性能。在下面的计时器中,它还将使用 x 毫秒的调用数填充地图。我将使用此映射作为直方图的一部分进行进一步分析,例如调