2009-05-18 21 views
123

Ich verwende einen statischen Code-Block, um einige Controller in einer Registrierung zu initialisieren, die ich habe. Meine Frage ist daher, kann ich garantieren, dass dieser statische Codeblock nur einmal beim ersten Laden der Klasse aufgerufen wird? Ich verstehe, dass ich nicht garantieren kann, wenn dieser Codeblock aufgerufen wird, ich rate es, wenn der Classloader ihn zuerst lädt. Ich weiß, dass ich die Klasse im statischen Codeblock synchronisieren konnte, aber ich vermute, dass das tatsächlich passiert, was auch immer passiert?Sind statische Java-Initialisierer threadsicher?

Einfaches Codebeispiel wäre;

class FooRegistry { 

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

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

oder sollte ich dies tun;

+9

Ich mag dieses Design nicht, da es nicht testbar ist. Sehen Sie sich Dependency Injection an. – dfa

Antwort

178

Ja, statische Java-Initialisierer sind Thread-sicher (verwenden Sie Ihre erste Option)

Wenn Sie jedoch sicherstellen möchten, dass der Code genau einmal ausgeführt wird, müssen Sie sicherstellen, dass die Klasse nur von einem einzigen Klassenlader geladen wird. Die statische Initialisierung wird einmal pro Klassenlader durchgeführt.

+2

:-) klar und direkt auf den Punkt - Prost! – simon622

+1

Eine Klasse kann jedoch von mehreren Klassenladeprogrammen geladen werden, so dass addController möglicherweise mehr als einmal aufgerufen wird (unabhängig davon, ob Sie den Anruf synchronisieren oder nicht) ... –

+4

Ah hängen Sie dann, also sagen wir, dass die statische Der Codeblock wird tatsächlich für jeden Klassenlader aufgerufen, der die Klasse lädt. Hmm ... Ich denke, das sollte immer noch in Ordnung sein, aber ich frage mich, wie das Ausführen dieser Art von Code in einer OSGI-Umgebung mit mehreren Bundle-Classloadern funktionieren würde. – simon622

3

Unter normalen Umständen passiert alles im statischen Initialisierer - vor allem, was diese Klasse verwendet, daher ist Synchronisation normalerweise nicht notwendig. Die Klasse ist jedoch für alles zugänglich, was der statische Initiator aufruft (einschließlich des Aufrufs anderer statischer Initialisierer).

Eine Klasse kann von einer geladenen Klasse geladen, aber nicht unbedingt sofort initialisiert werden. Natürlich kann eine Klasse durch mehrere Instanzen von Klassenladeprogrammen geladen werden und dadurch zu mehreren Klassen mit demselben Namen werden.

0

Ja, statische Initialisierer werden nur einmal ausgeführt. Read this for more information.

+1

Nein, sie können mehr als einmal ausgeführt werden. –

+2

Nein, sie können einmal pro CLASSLOADER ausgeführt werden. – ruurd

1

Ja, irgendwie

A static initializer nur einmal aufgerufen wird, so von dieser Definition Thread-sicher es ist - Sie zwei bräuchten oder mehr Anrufungen des static initializer sogar Thread-Konkurrenz zu bekommen.

Das gesagt, static Initialisierer sind auf viele andere Arten verwirrend. Es gibt wirklich keine bestimmte Reihenfolge, in der sie aufgerufen werden. Das wird sehr verwirrend, wenn Sie zwei Klassen haben, deren Initialisierer static voneinander abhängen. Und wenn Sie eine Klasse verwenden, aber nicht verwenden, was der Initialisierer static einrichten wird, können Sie nicht garantieren, dass der Klassenlader den statischen Initialisierer aufruft.

Denken Sie schließlich an die Objekte, auf denen Sie synchronisieren. Ich weiß, dass das nicht wirklich das ist, was du fragst, aber stelle sicher, dass deine Frage nicht wirklich fragt, ob du addController() Thread-sicher machen musst.

+3

Es gibt eine sehr definierte Reihenfolge, in der sie aufgerufen werden: Reihenfolge im Quellcode. – mafu

+0

Sie werden auch immer aufgerufen, egal ob Sie ihr Ergebnis verwenden. Wenn dies nicht in Java 6 geändert wurde. – mafu

+7

Innerhalb einer Klasse folgen Initialisierer dem Code. Bei zwei oder mehr Klassen ist es nicht so definiert, welche Klasse zuerst initialisiert wird, ob eine Klasse zu 100% initialisiert wird, bevor eine andere beginnt, oder wie Dinge "verschachtelt" werden. Z.B. Wenn zwei Klassen jeweils statische Initializer haben, werden die Dinge schnell hässlich. Ich dachte, es gibt Möglichkeiten, wie Sie auf eine statische final int zu einer anderen Klasse verweisen können, ohne die Initialisierer aufzurufen, aber ich werde den Punkt nicht auf die eine oder andere Art diskutieren – Matt

10

Dies ist ein Trick, den Sie für faule Initialisierung

enum Singleton { 
    INSTANCE; 
} 

oder für Pre Java 5,0

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

Da der statische Block in SingletonHolder einmal don Sie in einem Thread sichere Art und Weise laufen können‘ Ich brauche keine andere Verriegelung. Die Klasse SingletonHolder wird nur geladen, wenn Sie instance() aufrufen

+18

Sie basieren diese Antwort auf der Tatsache, dass der statische Block wird nur einmal global ausgeführt - genau diese Frage wurde gestellt. –

+2

Ich denke, das ist auch nicht sicher in Multi-Class-Loader-Umgebung, was sagen.? – Ahmad

+0

@Ahmad - soweit ich das verstehe, ja. Du hast Recht. –

-2

Also im Grunde, da Sie eine Singleton-Instanz wollen, sollten Sie es mehr oder weniger die altmodische Art tun und sicherstellen, dass Ihr Singleton-Objekt nur einmal initialisiert wird Einmal.

Verwandte Themen