2010-05-20 84 views
3

我有一些代码需要进行一次初始化。但是这段代码没有明确的生命周期,所以我的逻辑可能会在我的初始化完成之前被多个线程调用。所以,我想基本确保我的逻辑代码“等待”,直到初始化完成。启动时控制竞态条件

这是我的第一次切割。

public class MyClass { 
    private static final AtomicBoolean initialised = new AtomicBoolean(false); 

    public void initialise() { 
     synchronized(initialised) { 
      initStuff(); 
      initialised.getAndSet(true); 
      initialised.notifyAll(); 
     } 
    } 

    public void doStuff() { 
     synchronized(initialised) { 
      if (!initialised.get()) { 
       try { 
        initialised.wait(); 
       } catch (InterruptedException ex) { 
        throw new RuntimeException("Uh oh!", ex); 
       } 
      } 
     } 

     doOtherStuff(); 
    } 
} 

我基本上要确保这会做什么,我认为它会做的事情 - 块doStuff直到初始化是真实的,那我绝不错过的竞争条件,其中doStuff可能会被卡住在一个永远不会到达的Object.wait()上。

编辑:

我无法控制线程。我希望能够控制什么时候所有的初始化完成,这就是为什么doStuff()不能调用initialise()。

我使用了一个AtomicBoolean,因为它是一个值持有者和一个可以同步的对象的组合。我本来可以简单地有一个“公共静态最终对象锁=新对象();”和一个简单的布尔标志。 AtomicBoolean方便地给了我两个。布尔值不能被修改。

CountDownLatch正是我所期待的。我也考虑过使用0许可证的Sempahore。但CountDownLatch完美适用于这项任务。

+3

为什么不使用静态初始化器? – 2010-05-20 21:29:14

+0

初始化在哪里被调用? – luke 2010-05-20 21:30:22

+0

你是否在构造函数中启动线程来执行初始化工作? – rmarimon 2010-05-20 21:30:39

回答

6

这是一个奇怪的库和内置并发控制组合。像这样的东西是干净多了:

public class MyClass { 

    private static final CountDownLatch latch = new CountDownLatch(1); 

    public void initialise() { 
    initStuff(); 
    latch.countDown(); 
    } 

    public void doStuff() { 
    try { 
     latch.await(); 
    } catch (InterruptedException ex) { 
     throw new RuntimeException("Uh oh!", ex); 
    } 
    doOtherStuff(); 
    } 

} 
+0

+1我不知道那个,但它似乎非常有用 – karoberts 2010-05-20 21:43:19

+0

CountDownLatch在达到零时需要同步/锁定吗? – mdma 2010-05-20 21:43:52

+0

+1 - CountDownLatch.await在到达零时不会阻塞 - 只检查javadoc。 – mdma 2010-05-20 21:47:30

2

A​​块会自动阻塞其他线程。只需使用一个简单的锁定对象+状态变量:

public class MyClass { 
    private static boolean initialised; 
    private static final Object lockObject = new Object(); 

    public void initialise() { 
     synchronized (lockObject) { 
      if (!initialised) { 
       initStuff(); 
       initialised = true; 
      } 
     } 
    } 

    public void doStuff() { 
     initialise(); 
     doOtherStuff(); 
    } 
} 
+2

boolean doesn如果您始终从同步块内访问它,则不需要变化。 – mdma 2010-05-20 21:41:45

+0

哎呀,谢谢。我开始用双重锁定+易失性,然后决定这是不值得这个简单的情况下混淆。即使另一个答案被接受,也是固定的 – 2010-05-20 23:03:18

0

您使用AtomicBoolean总是从一个synchronized块内。没有多少意义,因为只有一个线程可以访问它。原子变量旨在用于无锁解决方案中 - 您可以将其设置为不间断单元。

我猜你正在寻找一个锁免费的解决方案,一旦intiialization发生:

public class MyClass { 
    private static final AtomicBoolean initialised = new AtomicBoolean(false); 

    public void initialise() { 
     if (!intialized.get()) 
     { 
      synchornized (this) 
      { 
       if (!initialized.getAndSet(true)) 
        doInitialize(); 
      } 
     } 
    } 

    public void doStuff() { 
     initialize(); 
     doOtherStuff(); 
    } 

你也可以用一个简单的volatile boolean这实际上是比的AtomicBoolean一点更有效的做到这一点。

0

这是在启动时正确,为什么不等到启动其他线程,直到初始化完成?另外,你可以做一个线程同步的IsComplete布尔值,它被设置为false,直到它被初始化例程设置为true。

1

最好的可能是使用一个静态初始化(如SB提及):

public class MyClass { 

    public static void doInitialize() { 
     ... 
    } 

    public void doStuff() { 
     doOtherStuff(); 
    } 

    static { 
     doInitialize(); 
    } 
} 

任何其他代码之前会执行这个操作一次允许为调用。如果您随时需要初始化该类,那么不会有性能问题,因为该类在使用之前不会被加载。有关更多详细信息,请参阅this question的答案。