在启动时控制竞争条件

时间:2010-05-20 21:21:43

标签: java concurrency

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

这是我的第一次削减。

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上的竞争条件。等待()永远不会到来。

编辑:

我无法控制线程。我希望能够控制所有初始化的时间,这就是doStuff()无法调用initialise()的原因。

我使用了AtomicBoolean,因为它是值持有者和我可以同步的对象的组合。我本可以简单地使用“public static final Object lock = new Object();”和一个简单的布尔标志。 AtomicBoolean方便地给了我两个。无法修改布尔值。

CountDownLatch正是我想要的。我还考虑过使用含有0许可证的Sempahore。但CountDownLatch非常适合这项任务。

5 个答案:

答案 0 :(得分: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();
  }

}

答案 1 :(得分:2)

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

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 :(得分:1)

最好的方法是使用静态初始化程序(如SB所述):

public class MyClass {

    public static void doInitialize() {
      ...
    }

    public void doStuff() {
        doOtherStuff();
    }

    static {
       doInitialize();
    }
}

在允许调用任何其他代码之前,这将执行一次。如果在使用该类的任何时候总是必须初始化,那么没有性能损失,因为在使用该类之前不会加载该类。有关详细信息,请参阅this question的答案。

答案 3 :(得分:0)

您始终在同步块内使用AtomicBoolean。没有太多意义,因为只有一个线程可以访问它。原子变量旨在用于无锁解决方案 - 您可以将该值设置为不间断单元。

我猜你正在寻找一个无锁解决方案,一旦发生了这种情况:

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更有效。

答案 4 :(得分:0)

启动时这是正确的,为什么不等待初始化完成之前启动其他线程?

此外,您可以执行线程同步的IsComplete布尔值,该布尔值设置为false,直到初始化例程将其设置为true。