I thread degli inizializzatori statici Java sono sicuri?


136

Sto usando un blocco di codice statico per inizializzare alcuni controller in un registro che ho. La mia domanda è quindi: posso garantire che questo blocco di codice statico venga chiamato una volta sola quando la classe viene caricata per la prima volta? Capisco che non posso garantire quando verrà chiamato questo blocco di codice, immagino che sia quando Classloader lo carica per la prima volta. Mi rendo conto che potrei sincronizzarmi sulla classe nel blocco di codice statico, ma la mia ipotesi è che in realtà è quello che succede comunque?

Sarebbe un semplice esempio di codice;

class FooRegistry {

    static {
        //this code must only ever be called once 
        addController(new FooControllerImpl());
    }

    private static void addController(IFooController controller) { 
        // ...
    }
}

o dovrei farlo;

class FooRegistry {

    static {
        synchronized(FooRegistry.class) {
            addController(new FooControllerImpl());
        }
    }

    private static void addController(IFooController controller) {
        // ...
    }
}

10
Non mi piace questo design, dal momento che non è testabile. Dai un'occhiata a Dependency Injection.
dfa,

Risposte:


199

Sì, gli inizializzatori statici Java sono thread-safe (usa la tua prima opzione).

Tuttavia, se si desidera assicurarsi che il codice venga eseguito esattamente una volta, è necessario assicurarsi che la classe sia caricata solo da un singolo caricatore di classi. L'inizializzazione statica viene eseguita una volta per caricatore di classi.


2
Tuttavia, una classe può essere caricata da più caricatori di classi, quindi addController può comunque essere chiamato più di una volta (indipendentemente dal fatto che si sincronizzi o meno la chiamata) ...
Matthew Murdoch,

4
Ah aspetta allora, quindi stiamo dicendo che il blocco di codice statico viene effettivamente chiamato per ogni classloader che carica la classe. Hmm ... Immagino che dovrebbe essere ancora ok, tuttavia, mi chiedo come funzionerebbe questo tipo di codice in un ambiente OSGI, con
caricatori di classi

1
Sì. Il blocco di codice statico viene chiamato per ogni caricatore di classi che carica la classe.
Matthew Murdoch,

3
@ simon622 Sì, ma funzionerebbe in un oggetto classe diverso in ogni ClassLoader. Oggetti di classe diversi che hanno ancora lo stesso nome completo, ma rappresentano tipi diversi che non possono essere trasmessi l'uno all'altro.
Erwin Bolwidt,

1
significa che la parola chiave 'final' è ridondante nel proprietario dell'istanza in: en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom ?
spc16670,

11

Questo è un trucco che puoi usare per l'inizializzazione lenta

enum Singleton {
    INSTANCE;
}

o per pre Java 5.0

class Singleton {
   static class SingletonHolder {
      static final Singleton INSTANCE = new Singleton();
   }
   public static Singleton instance() {
      return SingletonHolder.INSTANCE;
   }
}

Poiché il blocco statico in SingletonHolder verrà eseguito una volta in maniera thread-safe, non è necessario alcun altro blocco. La classe SingletonHolder verrà caricata solo quando chiami istanza ()


18
Stai basando questa risposta sul fatto che il blocco statico verrà eseguito una sola volta a livello globale, che è la vera domanda che è stata posta.
Michael Myers

2
Penso che anche questo non sia sicuro in un ambiente caricatore multi-classe che dire.
Ahmad,

2
@Ahmad Gli ambienti di caricamento multi-classe sono progettati per consentire a ciascuna applicazione di avere i propri singleton.
Peter Lawrey,

4

In circostanze normali accade tutto nell'inizializzatore statico, prima di tutto ciò che usa quella classe, quindi la sincronizzazione non è di solito necessaria. Tuttavia, la classe è accessibile a tutto ciò che chiama l'intiailizzatore statico (incluso il fatto di invocare altri inizializzatori statici).

Una classe può essere caricata da una classe caricata ma non necessariamente inizializzata immediatamente. Naturalmente, una classe può essere caricata da istanze multiple di caricatori di classi e quindi diventare più classi con lo stesso nome.


3

Sì, una specie di

Un staticinizializzatore viene chiamato solo una volta, quindi in base a tale definizione è thread-safe: staticper ottenere la contesa del thread sono necessarie due o più chiamate dell'inizializzatore.

Detto questo, gli staticinizializzatori creano confusione in molti altri modi. Non c'è davvero nessun ordine specificato in cui vengono chiamati. Questo diventa davvero confuso se hai due classi i cui staticinizializzatori dipendono l'uno dall'altro. E se usi una classe ma non usi ciò che l' staticinizializzatore imposterà, non sei sicuro che il caricatore di classi invocherà l'inizializzatore statico.

Infine, tieni a mente gli oggetti su cui stai sincronizzando. Mi rendo conto che non è proprio quello che stai chiedendo, ma assicurati che la tua domanda non stia davvero chiedendo se devi rendere addController()sicuro il thread.


5
Esiste un ordine ben definito in cui vengono chiamati: Per ordine nel codice sorgente.
mafu,

Inoltre, vengono sempre chiamati, non importa se si utilizza il loro risultato. A meno che ciò non sia stato modificato in Java 6.
mafu

8
All'interno di una classe, gli inizializzatori seguono il codice. Date due o più classi, non è così definito come quale classe viene inizializzata per prima, se una classe viene inizializzata al 100% prima dell'inizio di un'altra o come le cose vengono "interfogliate". Ad esempio, se due classi hanno ciascuna inialializer statici che si riferiscono l'una all'altra, le cose diventano brutte in fretta. Pensavo ci fossero dei modi in cui potevi riferirti a un finale statico in un'altra classe senza invocare gli inizializzatori, ma non discuterò il punto in un modo o nell'altro
Matt,

Diventa brutto e lo eviterei. Ma esiste un modo definito per la risoluzione dei cicli. Citando "The Java Programming Language 4th Edition": Pagina: 75, Sezione: 2.5.3. Inizializzazione statica: "Se si verificano cicli, gli inizializzatori statici di X saranno stati eseguiti solo fino al punto in cui è stato invocato il metodo Y. Quando Y, a sua volta, invoca il metodo X, quel metodo viene eseguito con il resto degli inizializzatori statici ancora da eseguire "
JMI MADISON,

0

Sì, gli inizializzatori statici vengono eseguiti una sola volta. Leggi questo per maggiori informazioni .


2
No, possono essere eseguiti più di una volta.
Espiazione limitata il

5
No, possono essere eseguiti una volta PER CLASSLOADER.
Ruurd,

Risposta di base: init statico viene eseguito una sola volta. Risposta avanzata: init statico viene eseguito una volta per caricatore di classi. Il primo commento è confuso perché il fraseggio mescola queste due risposte.
JMI MADISON,

-4

Quindi, fondamentalmente, poiché desideri un'istanza singleton, dovresti farlo più o meno alla vecchia maniera e assicurarti che l'oggetto singleton sia inizializzato una volta e una sola volta.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.