我正在寻找类似问题的答案,使用 AtomicReference.compareAndSet 设置对数据库调用结果的引用是否合适?但有不同的要求。
目标是只创建一次实例ObjectWithSideEffectConstructor
以避免重复的副作用。施工必须发生在setUp()
. 多个线程将调用setUp()
. 同样会有一个tearDown()
用于从对象中回收资源,这里省略。问题:实现目标的最佳实践是什么?
仅仅使用AtomicReference
是不够的,因为会先执行构造函数,所以会产生副作用。
private static AtomicReference<ObjectWithSideEffectConstructor> ref =
new AtomicReference<ObjectWithSideEffectConstructor>()
void setUp() {
ref.compareAndSet(null, new ObjectWithSideEffectConstructor());
}
使用是否适合使用 AtomicReference.compareAndSet 来设置对数据库调用结果的引用的答案?不会工作,因为volatile
缺乏同步。会有多个线程进入的窗口if
。
private static volatile ObjectWithSideEffectConstructor obj;
void setUp() {
if (obj == null) obj = new ObjectWithSideEffectConstructor();
}
简单的修复将是
private static ObjectWithSideEffectConstructor obj;
private static final Object monitor = new Object();
void setUp() {
synchronized (monitor) {
if (obj == null) obj = new ObjectWithSideEffectConstructor();
}
}
同样,具有易失性监视器的 DCL 可能会提供更好的读取性能。但两者都需要某种程度的同步,因此预计性能会更差。
我们也可以使用FutureTask
. 它更高效,因为一旦创建了对象,后续FutureTask.get()
将返回而不会阻塞。但它肯定比synchronized
.
private static final AtomicReference<FutureTask<ObjectWithSideEffectConstructor>> ref =
new AtomicReference<FutureTask<ObjectWithSideEffectConstructor>>();
void setUp() {
final FutureTask<ObjectWithSideEffectConstructor> future =
new FutureTask<ObjectWithSideEffectConstructor>(
new Callable<ObjectWithSideEffectConstructor>() {
@Override
public ObjectWithSideEffectConstructor call() throws InterruptedException {
return new ObjectWithSideEffectConstructor();
}
}
);
if (ref.compareAndSet(null, future)) future.run();
ref.get().get();
}
感谢您的建议。