2010-05-20 2 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(), который никогда не появится.

Edit:

Я не имею никакого контроля над потоками. И я хочу иметь возможность контролировать, когда вся инициализация выполнена, поэтому doStuff() не может вызвать initialise().

Я использовал AtomicBoolean, поскольку это была комбинация держателя значений, и объект, который я мог бы синхронизировать. Я мог бы также просто иметь «общедоступный статический окончательный объект Lock = new Object();» и простой булевский флаг. AtomicBoolean удобно дал мне обоим. Логическое значение не может быть изменено.

CountDownLatch - именно то, что я искал. Я также рассмотрел использование Sempahore с 0 разрешениями. Но CountDownLatch идеально подходит для этой задачи.

+3

Почему бы не использовать статический инициализатор? –

+0

где инициализируется вызов? – luke

+0

Вы запускаете поток в своем конструкторе для выполнения инициализации или нет? – rmarimon

ответ

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

+0

делает ли CountDownLatch синхронизацию/блокировку, когда она достигает нуля? – mdma

+0

+1 - CountDownLatch.await не блокируется, когда он достигает нуля - только что проверил javadocs. – mdma

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

boolean doesn 't должен быть volatile, если вы всегда получаете доступ к нему изнутри синхронизированного блока. – mdma

+0

Упс, спасибо. Я начал с двойной проверки блокировки + volatile, а затем решил, что это не стоит путаницы для этого простого сценария. Исправлено, хотя был принят другой ответ. –

0

Вы с помощью AtomicBoolean всегда внутри синхронизированного блока. Это не так много, потому что только один поток может получить к нему доступ. Атомные переменные предназначены для использования в незакрепленных решениях - вы можете получить и установить значение как источник бесперебойного питания.

Я предполагаю, что вы ищете блокировки свободного решения, как только 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.

Смежные вопросы