java - 确保只有一个线程初始化对象

时间:2014-06-12 16:26:39

标签: java multithreading

我有一个小问题,确保对象的初始化由一个线程完成,只有一次,如下面的代码片段所示:

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块中包装倒计时,因为如果异常发生,线程mnight会卡住。但是,即使使用finally块,线程也会被释放,系统将处于未初始化状态。为了做到这一点,是否有一种模式可以遵循?

4 个答案:

答案 0 :(得分:1)

我建议从一个简单而正确的解决方案开始。如果性能真的很重要,那么您可以在之后进行优化。一个简单的解决方案可能如下所示:

private final Object lock = new Object();
private boolean initialized = false;

void init() {
    synchronized (lock) {
        if (!initialized) {
            someMethod();
            initialized = true;
        }
    }
}

如果性能很重要,您可以添加一个布尔变量的附加检查,它使用 volatile 变量而不是synchronized块。此模式称为双重检查锁定

private final Object lock = new Object();
private volatile boolean initialized = false;

void init() {
    if (!initialized) {
        synchronized (lock) {
            if (!initialized) {
                someMethod();
                initialized = true;
            }
        }
    }
}

答案 1 :(得分:0)

你不能做这样的事情:

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;
}

答案 2 :(得分:0)

在Java中,您可能希望使用“按需初始化持有者习惯用法”

http://en.wikipedia.org/wiki/Singleton_pattern#Initialization_On_Demand_Holder_Idiom

答案 3 :(得分:0)

初始化状态的无锁方法可以使用保持状态的AtomicReference:

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;
   }

你可能想要在这里保持吸气的原因是你可以稍后改变主意使用另一个习语。