web-dev-qa-db-fra.com

Java les initialiseurs statiques sont-ils sûrs pour les threads?

J'utilise un bloc de code statique pour initialiser certains contrôleurs dans un registre que j'ai. Ma question est donc, puis-je garantir que ce bloc de code statique ne sera absolument appelé qu'une seule fois lors du premier chargement de la classe? Je comprends que je ne peux pas garantir quand ce bloc de code sera appelé, je suppose que c'est quand le Classloader le charge pour la première fois. Je me rends compte que je pourrais synchroniser sur la classe dans le bloc de code statique, mais je suppose que c'est en fait ce qui se passe de toute façon?

Un exemple de code simple serait;

class FooRegistry {

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

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

ou devrais-je le faire;

class FooRegistry {

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

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

Oui, Java sont thread-safe (utilisez votre première option).

Cependant, si vous voulez vous assurer que le code est exécuté exactement une fois, vous devez vous assurer que la classe n'est chargée que par un seul chargeur de classe. L'initialisation statique est effectuée une fois par chargeur de classe.

195
Matthew Murdoch

C'est une astuce que vous pouvez utiliser pour l'initialisation paresseuse

enum Singleton {
    INSTANCE;
}

ou pour pre Java 5.0

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

Comme le bloc statique dans SingletonHolder s'exécutera une fois de manière sécurisée pour les threads, vous n'avez pas besoin d'autre verrouillage. La classe SingletonHolder ne sera chargée que lorsque vous appelez instance ()

11
Peter Lawrey

Dans les circonstances habituelles, tout dans l'initialiseur statique se produit avant tout ce qui utilise cette classe, donc la synchronisation n'est généralement pas nécessaire. Cependant, la classe est accessible à tout ce que l'intiailiser statique appelle (y compris l'invocation d'autres initialiseurs statiques).

Une classe peut être chargée par une classe chargée mais pas nécessairement initialisée immédiatement. Bien sûr, une classe peut être chargée par plusieurs instances de chargeurs de classe et devenir ainsi plusieurs classes avec le même nom.

4

Oui, en quelque sorte

Un initialiseur static n'est appelé qu'une seule fois, donc selon cette définition, il est sûr pour les threads - vous auriez besoin de deux appels ou plus de l'initialiseur static pour même obtenir la contention des threads.

Cela dit, les initialiseurs static sont source de confusion à bien d'autres égards. Il n'y a vraiment aucun ordre spécifié dans lequel ils sont appelés. Cela devient vraiment déroutant si vous avez deux classes dont les initialiseurs static dépendent l'un de l'autre. Et si vous utilisez une classe mais n'utilisez pas ce que l'initialiseur static va configurer, vous n'êtes pas assuré que le chargeur de classe invoquera l'initialiseur statique.

Enfin, gardez à l'esprit les objets sur lesquels vous effectuez la synchronisation. Je me rends compte que ce n'est pas vraiment ce que vous demandez, mais assurez-vous que votre question ne demande pas vraiment si vous devez rendre addController() thread-safe.

3
Matt

Oui, les initialiseurs statiques ne sont exécutés qu'une seule fois. Lisez ceci pour plus d'informations .

0
Mike Pone