wake-up-neo.com

Sind Java statische Initialisierer threadsicher?

Ich verwende einen statischen Codeblock, 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 absolut aufgerufen wird? Ich kann nicht garantieren, wann dieser Codeblock aufgerufen wird. Ich schätze, wann der Classloader ihn zum ersten Mal lädt. Mir ist klar, dass ich die Klasse im statischen Codeblock synchronisieren kann, aber meine Vermutung ist, dass dies tatsächlich der Fall ist?

Ein 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 soll ich das tun;

class FooRegistry {

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

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

Ja, Java statische Initialisierer sind threadsicher (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 einzelnen Klassenladeprogramm geladen wird. Die statische Initialisierung wird einmal pro Klassenladeprogramm durchgeführt.

195
Matthew Murdoch

Dies ist ein Trick, den Sie für die verzögerte Initialisierung verwenden können

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 threadsicher ausgeführt wird, benötigen Sie keine weitere Sperre. Die Klasse SingletonHolder wird nur geladen, wenn Sie instance () aufrufen.

11
Peter Lawrey

Unter normalen Umständen geschieht alles im statischen Initialisierer - vor allem, was diese Klasse verwendet, sodass normalerweise keine Synchronisierung erforderlich ist. Auf die Klasse kann jedoch auf alle vom statischen Intiailiser aufgerufenen Elemente zugegriffen werden (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.

Mehr oder weniger

Ein static -Initialisierer wird nur einmal aufgerufen, daher ist es nach dieser Definition threadsicher - Sie benötigen zwei oder mehr Aufrufe des static -Initialisierers, um überhaupt einen Thread-Konflikt zu erhalten.

Das heißt, static Initialisierer sind auf viele andere Arten verwirrend. Es gibt wirklich keine festgelegte Reihenfolge, in der sie aufgerufen werden. Dies wird sehr verwirrend, wenn Sie zwei Klassen haben, deren static -Initialisierer voneinander abhängig sind. Und wenn Sie eine Klasse verwenden, aber nicht das verwenden, was der Initialisierer static eingerichtet hat, kann nicht garantiert werden, dass der Klassenlader den statischen Initialisierer aufruft.

Denken Sie abschließend an die Objekte, mit denen Sie synchronisieren. Mir ist klar, dass dies nicht wirklich das ist, wonach Sie fragen, aber stellen Sie sicher, dass Ihre Frage nicht wirklich fragt, ob Sie addController() thread-safe machen müssen.

3
Matt

Ja, statische Initialisierer werden nur einmal ausgeführt. Lesen Sie dies für weitere Informationen .

0
Mike Pone