@ Rozwiązanie ShayHaned wykorzystuje blokowanie. Można uczynić go bardziej skutecznym poprzez AtomicBoolean
jak:
AtomicBoolean wasRun = new AtomicBoolean(false);
CountDownLatch initCompleteLatch = new CountDownLatch(1);
public void initialize() {
if (!wasRun.getAndSet(true)) {
List<Metadata> metadata = getMetadata(true);
List<Process> process = getProcess();
if (!metadata.isEmpty() && !process.isEmpty()) {
Manager.setAllMetadata(metadata, process);
}
startBackgroundThread();
initCompleteLatch.countDown();
} else {
log.info("Waiting to ensure initialize is done.");
initCompleteLatch.await();
log.warn("I was already run");
}
}
Powyższe zakłada, że nie trzeba czekać do pracy w startBackgroundThread
aby zakończyć. Jeśli tak, rozwiązanie staje się:
AtomicBoolean wasRun = new AtomicBoolean(false);
CountDownLatch initCompleteLatch = new CountDownLatch(1);
public void initialize() {
if (!wasRun.getAndSet(true)) {
List<Metadata> metadata = getMetadata(true);
List<Process> process = getProcess();
if (!metadata.isEmpty() && !process.isEmpty()) {
Manager.setAllMetadata(metadata, process);
}
// Pass the latch to startBackgroundThread so it can
// call countDown on it when it's done.
startBackgroundThread(initCompleteLatch);
} else {
log.info("Waiting to ensure initialize is done.");
initCompleteLatch.await();
log.warn("I was already run");
}
}
Powodem tego jest to, że działa AtomicBoolean.getAndSet(true)
będzie w jednej operacji atomowych, zwraca wartość, która została uprzednio ustawioną i sprawiają, że nowa wartość będzie true
. Tak więc pierwszy wątek, który dostanie się do twojej metody, zostanie zwrócony false
(ponieważ zmienna została zainicjalizowana na wartość false) i ustawi ją atomicznie na wartość true. Ponieważ ten pierwszy wątek został zwrócony fałszywie, zajmie on pierwszą gałąź w oświadczeniu if
i nastąpi inicjalizacja. Wszelkie inne połączenia będą wykrywać, że wasRun.getAndSet
zwraca true
od pierwszego wątku ustawionego na true, więc zajmą drugą gałąź, a otrzymasz komunikat dziennika, który chcesz.
CountDownLatch jest inicjowany do 1 tak wszystkich wątków innych niż pierwszego połączenia await
na nim. Będą blokować, dopóki pierwszy wątek nie zadzwoni pod numer countDown
, który ustawi liczbę na 0, zwalniając wszystkie oczekujące wątki.
Jeśli chcesz się upewnić, że kawałek kodu wykonywana jest dokładnie raz, niż myślę, że wprowadzenie go w klasie statycznej inicjatora enum jest tak blisko, jak to tylko możliwe otrzymać. JVM gwarantuje, że zostanie to wywołane co najwyżej raz dla każdego programu ładującego klasy. Ale nie sądzę, że istnieje jasny sposób na udzielenie takiej gwarancji. Może gdybyś podał więcej informacji na temat twojego przypadku użycia? – korolar
Mam tę metodę w jednej z moich klas, która inicjalizuje wszystkie nasze metadane i po zakończeniu inicjalizacji, tylko chcę przejść do przodu w mojej aplikacji. – user1950349
Czy wprowadzenie go w inicjalizatorze statycznym tej klasy nie wystarczy? – korolar