开发者

Controlling race condition at startup

I have some code that I want to have some one time initialisation performed. But this code doesn't have a definite lifecycle, so my logic can be potentially invoked by multiple threads before my initialisation is done. So, I want to basically ensure that my logic code "waits" until initialisation is done.

This is my first cut.

public class MyClass {
    private static f开发者_如何学Goinal 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();
    }
}

I basically want to make sure this is going to do what I think it's going to do -- block doStuff until the initialised is true, and that I'm not missing a race condition where doStuff might get stuck on a Object.wait() that will never arrive.

Edit:

I have no control over the threads. And I want to be able to control when all of the initialisation is done, which is why doStuff() can't call initialise().

I used an AtomicBoolean as it was a combination of a value holder, and an object I could synchronize. I could have also simply had a "public static final Object lock = new Object();" and a simple boolean flag. AtomicBoolean conveniently gave me both. A Boolean can not be modified.

The CountDownLatch is exactly what I was looking for. I also considered using a Sempahore with 0 permits. But the CountDownLatch is perfect for just this task.


That's a strange mix of library and built-in concurrency controls. Something like this is much cleaner:

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

}


A synchronized block will automatically block other threads. Just use a simple lock object + status variable:

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


The best may be to use a static initializer (as mentioned by SB):

public class MyClass {

    public static void doInitialize() {
      ...
    }

    public void doStuff() {
        doOtherStuff();
    }

    static {
       doInitialize();
    }
}

This will get executed once before any other code is allowed to be called. If you will always have to initialize anytime the class is used then there is no performance hit as the class will not be loaded until it is used. See the answers to this question for more details.


It this is right at startup, why not wait to start the other threads until the initialization is complete?

Also, you can do a thread-synchronized IsComplete boolean that is set to false until it is set to true by the initialization routine.


You're using AtomicBoolean always from inside a synchronized block. There's not much point to that since only one thread can access it. Atomic variables are intended for use in lock-free solutions - you can get and set the value as an uninterruptable unit.

I guess you are looking for a lock free solution once the intiialization has happened:

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

You could also do this with a simple volatile boolean which is actually a little more efficient than an AtomicBoolean.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜