代码之家  ›  专栏  ›  技术社区  ›  Will Hartung

控制启动时的竞争条件

  •  3
  • Will Hartung  · 技术社区  · 14 年前

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

    这是我的第一刀。

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

    我基本上想确保这将做我认为它将要做的事情——阻止dostuf直到初始化为真,并且我不会错过dostuf可能卡在对象上的争用条件。wait()这将永远不会到达。

    编辑:

    我无法控制这些线。我希望能够控制所有初始化何时完成,这就是dostuff()不能调用initialise()的原因。

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

    我正在找的正是倒计时。我还考虑使用一个拥有0个许可证的Sempahore。但是,对于这项任务来说,倒数计时是完美的。

    5 回复  |  直到 14 年前
        1
  •  6
  •   erickson    14 年前

    这是库和内置并发控件的奇怪组合。像这样的东西更干净:

    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();
      }
    
    }
    
        2
  •  2
  •   Brett Kail    14 年前

    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();
        }
    }
    
        3
  •  1
  •   Community CDub    7 年前

    最好是使用静态初始值设定项(如sb所述):

    public class MyClass {
    
        public static void doInitialize() {
          ...
        }
    
        public void doStuff() {
            doOtherStuff();
        }
    
        static {
           doInitialize();
        }
    }
    

    这将在允许调用任何其他代码之前执行一次。如果每次使用该类时都必须进行初始化,则不会影响性能,因为在使用该类之前不会加载该类。看看答案 this question 了解更多详细信息。

        4
  •  0
  •   Jason Kleban    14 年前

    如果这在启动时是正确的,为什么不等到初始化完成后再启动其他线程呢?

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

        5
  •  0
  •   mdma    14 年前

    你在用 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 它实际上比原子布尔函数效率高一点。