- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
在某种程度上,这个问题相当简单。假设我有这个类:
static class Singleton {
}
我想为它提供一个单例工厂。我可以做(可能)显而易见的事情。我不会提及枚举可能性或任何其他,因为它们对我不感兴趣。
static final class SingletonFactory {
private static volatile Singleton singleton;
public static Singleton getSingleton() {
if (singleton == null) { // volatile read
synchronized (SingletonFactory.class) {
if (singleton == null) { // volatile read
singleton = new Singleton(); // volatile write
}
}
}
return singleton; // volatile read
}
}
我可以摆脱一个
volatile read
以更高的代码复杂度为代价:
public static Singleton improvedGetSingleton() {
Singleton local = singleton; // volatile read
if (local == null) {
synchronized (SingletonFactory.class) {
local = singleton; // volatile read
if (local == null) {
local = new Singleton();
singleton = local; // volatile write
}
}
}
return local; // NON volatile read
}
这几乎就是我们的代码近十年来一直在使用的东西。
release/acquire
让这更快吗?
java-9
中添加的语义通过
VarHandle
:
static final class SingletonFactory {
private static final SingletonFactory FACTORY = new SingletonFactory();
private Singleton singleton;
private static final VarHandle VAR_HANDLE;
static {
try {
VAR_HANDLE = MethodHandles.lookup().findVarHandle(SingletonFactory.class, "singleton", Singleton.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Singleton getInnerSingleton() {
Singleton localSingleton = (Singleton) VAR_HANDLE.getAcquire(FACTORY); // acquire
if (localSingleton == null) {
synchronized (SingletonFactory.class) {
localSingleton = (Singleton) VAR_HANDLE.getAcquire(FACTORY); // acquire
if (localSingleton == null) {
localSingleton = new Singleton();
VAR_HANDLE.setRelease(FACTORY, localSingleton); // release
}
}
}
return localSingleton;
}
}
这会是一个有效且正确的实现吗?
最佳答案
是的,这是正确的,并且存在 on Wikipedia . (该字段是否易变并不重要,因为它只能从 VarHandle
访问。)
如果第一次读取看到一个过时的值,它将进入同步块(synchronized block)。由于同步块(synchronized block)涉及先发生关系,因此第二次读取将始终看到写入的值。即使在维基百科上,它也说顺序一致性丢失了,但它指的是字段;同步块(synchronized block)是顺序一致的,即使它们使用发布-获取语义。
所以第二次空检查永远不会成功,并且对象永远不会被实例化两次。
保证第二次读取将看到写入的值,因为它是在与计算值并存储在变量中时持有相同的锁的情况下执行的。
在 x86 上,所有负载都具有获取语义,因此唯一的开销是空检查。 Release-acquire 允许最终看到值(这就是为什么相关方法在 Java 9 之前被称为 lazySet
,并且它的 Javadoc 使用了完全相同的词)。在这种情况下,同步块(synchronized block)可以防止这种情况发生。
指令可能不会被重新排序到同步块(synchronized block)中。
关于java - 没有 volatile 的双重检查锁定(但使用 VarHandle 释放/获取),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65171886/
我一直在努力理解 VarHandle::setOpaque 和 VarHandle::getOpaque 真正在做什么。到目前为止,这并不容易 - 我认为有些东西我得到了(但不会在问题本身中提出它们,
我正在尝试转换一些代码,这些代码使用 Unsafe 来对类中的局部变量执行内存访问,而且这些代码似乎也使用 Unsafe 来访问数组中的元素。 我有以下代码为单个元素创建 VarHandle,它似乎可
在 JEP193 ,VarHandles 的具体目标之一是提供使用 FieldUpdaters 和 AtomicIntegers 的替代方案(并避免与它们相关的一些开销)。 AtomicInteger
我一直在研究 Java 9 的一些新功能,但没有找到任何有用且实用的示例。 考虑下一个创建VarHandle的代码片段: class Counter { int i; } class VarH
在某种程度上,这个问题相当简单。假设我有这个类: static class Singleton { } 我想为它提供一个单例工厂。我可以做(可能)显而易见的事情。我不会提及枚举可能性或任何其他,因为它
VarHandle 显示以下错误 - Exception in thread "main" java.lang.NoSuchMethodError: VarHandle.compareAndSet(V
我想使用 panama 项目中的外部函数接口(interface)从 Java19 访问 C 库。 C 接口(interface)非常简单: typedef struct { int len;
我想使用 panama 项目中的外部函数接口(interface)从 Java19 访问 C 库。 C 接口(interface)非常简单: typedef struct { int len;
我是一名优秀的程序员,十分优秀!