2010-05-20 82 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的答案。