java-确保只有一个线程初始化对象
我有一个小问题,就是确保对象的初始化是由一个线程完成的,并且只执行一次,即下面给出的代码片段:java-确保只有一个线程初始化对象,java,multithreading,Java,Multithreading,我有一个小问题,就是确保对象的初始化是由一个线程完成的,并且只执行一次,即下面给出的代码片段: AtomicBoolean initialize = new AtomicBoolean(); CountDownLoatch latch = new CountDownLatch(1); void init(){ if(initialize.compareAndSwap(false,true)) { someMethod() // this can throw so
AtomicBoolean initialize = new AtomicBoolean();
CountDownLoatch latch = new CountDownLatch(1);
void init(){
if(initialize.compareAndSwap(false,true)) {
someMethod() // this can throw some exception
latch.countDown();
}
else{
latch.await();
}
}
我可以将倒计时包装在finally块中,因为如果发生异常,线程可能会被卡住。但是,即使使用finally块,线程也将被释放,系统将处于未初始化状态。是否有一种模式可以遵循以正确地实现这一点?难道你就不能这样做:
private static boolean initialized = false;
public synchronized void init() {
if(initialized){return}
if(initialize.compareAndSwap(false,true)) {
someMethod() // this can throw some exception
latch.countDown();
}
else{
latch.await();
}
initialized = true;
}
我建议从一个简单正确的解决方案开始。如果性能真的很重要,您可以在以后对其进行优化。一个简单的解决方案可能如下所示:
private final Object lock = new Object();
private boolean initialized = false;
void init() {
synchronized (lock) {
if (!initialized) {
someMethod();
initialized = true;
}
}
}
如果性能很重要,您可以添加对布尔变量的附加检查,该检查使用易失性变量而不是同步块。这种模式称为双重检查锁定:
在Java中,您可能希望使用“按需初始化持有者习惯用法”
初始化状态的无锁方法可以使用保存状态的原子引用:
private static AtomicReference<Object> LAZY_INITIALIZED = new AtomicReference<>();
public static Object getLazyInitialized() {
Object result = LAZY_INITIALIZED.get();
if (result != null)
return result;
result = new Object(...); // all initialization takes place here
if (LAZY_INITIALIZED.compareAndSet(null, result)) {
return result;
} else {
// another thread beat us to it, throw away our state and use the
// state that finished construction first
return LAZY_INITIALIZED.get();
}
}
您可能希望将getter保留在此处的原因是,您可以稍后改变主意,使用另一个惯用法。如果
someMethod()
抛出异常,您希望该行为是什么?另一个线程是否可以再次尝试?在初始化块上同步。它是静态块吗?如果不是,不同的线程是否从不同的对象调用此代码?在任何一种情况下,另一个线程都不知道另一个线程中的错误-我必须将其存储在某个易失性/原子变量中,然后重试/失败,我猜..其他线程是否也需要访问此对象,假设它是由一个线程正确初始化的?如果是,这看起来像是Singleton的作业,IMO。在我的示例中,NoSign将使用状态,直到初始化的线程调用latch.countdown并向其他线程发出信号表明它已完成
private static AtomicReference<Object> LAZY_INITIALIZED = new AtomicReference<>();
public static Object getLazyInitialized() {
Object result = LAZY_INITIALIZED.get();
if (result != null)
return result;
result = new Object(...); // all initialization takes place here
if (LAZY_INITIALIZED.compareAndSet(null, result)) {
return result;
} else {
// another thread beat us to it, throw away our state and use the
// state that finished construction first
return LAZY_INITIALIZED.get();
}
}
private static final EAGER_INITIALIZED = new Object(...);
public static getState() {
return EAGER_INITIALIZED;
}