Sécurité du fil à Singleton

je comprends que le double verrouillage en Java est cassé, alors quelles sont les meilleures façons de rendre le Thread Singletons sûr en Java? La première chose qui me vient à l'esprit est:

class Singleton{
    private static Singleton instance;

    private Singleton(){}

    public static synchronized Singleton getInstance(){
        if(instance == null) instance = new Singleton();
        return instance;
    }
}

est-ce que ça marche? si oui, est - ce la meilleure façon (je suppose que cela dépend des circonstances, de sorte qu'indiquer quand une technique particulière est la meilleure, serait utile)

13
demandé sur Robert 2010-05-26 15:04:55

5 réponses

Josh Bloch recommande l'utilisation d'un seul élément enum type pour mettre en œuvre singletons (voir Java Effective 2nd Edition, Item 3: Enforce the singleton property with a private constructor or an enum type ).

certains pensent qu'il s'agit d'un piratage, car il ne transmet pas clairement l'intention, mais il fonctionne.

l'exemple suivant est tiré directement du livre.

public enum Elvis {
   INSTANCE;

   public void leaveTheBuilding() { ... }
}

ici est sa plaidoirie finale:

Cette approche [...] est plus concis, fournit gratuitement les machines de sérialisation, et fournit une garantie ironclad contre les instanciations multiples, même en cas d'attaques sophistiquées de sérialisation ou de réflexion. Bien que cette approche n'ait pas encore été largement adoptée, un enum à un seul élément est la meilleure façon de mettre en œuvre un .


Sur enum constante singleton garantie

JLS 8.9. Enum

un type enum n'a pas d'instances autres que celles définies par ses constantes enum. C'est une erreur de compilation pour tenter d'instancier explicitement un type enum (§15.9.1).

la méthode final clone dans Enum garantit que les constantes d'enum ne peuvent jamais être clonées, et le traitement spécial par le le mécanisme de sérialisation garantit que les instances dupliquées ne sont jamais créées à la suite de la desérialisation. L'instanciation réfléchissante des types enum est interdite. Ensemble, ces quatre choses assurent qu'aucune instance de type enum n'existe au-delà de celles définies par les constantes enum.


initialisation

l'extrait suivant:

public class LazyElvis {
    enum Elvis {
        THE_ONE;
        Elvis() {
            System.out.println("I'M STILL ALIVE!!!");
        }       
    }
    public static void main(String[] args) {
        System.out.println("La-dee-daaa...");
        System.out.println(Elvis.THE_ONE);
    }
}

produit: sortie:

La-dee-daaa...
I'M STILL ALIVE!!!
THE_ONE

comme vous pouvez le voir, THE_ONE constante n'est pas instanciée à travers le constructeur jusqu'à la première fois qu'il est accédé.

25
répondu polygenelubricants 2013-06-14 04:29:14

Je ne vois aucun problème avec votre implémentation (autre que le fait que la serrure pour le singleton-monitor peut être utilisée par d'autres méthodes, pour d'autres raisons, et donc, inutilement, empêcher un autre thread d'obtenir l'instance). Cela pourrait être évité en introduisant un Object lock supplémentaire à verrouiller.

cet article de Wikipédia suggère une autre méthode:

public class Something {
    private Something() {
    }

    private static class LazyHolder {
        private static final Something INSTANCE = new Something();
    }

    public static Something getInstance() {
        return LazyHolder.INSTANCE;
    }
}

De l'article:

cette implémentation est une implémentation bien performante et concurrente valide dans toutes les versions de Java.

...

La mise en œuvre repose sur la phase d'initialisation bien spécifiée de L'exécution dans la machine virtuelle Java (JVM); voir la section 12.4 de Java Language Specification (JLS) pour plus de détails.

8
répondu aioobe 2010-05-26 11:13:48

Ma préférence est de le faire:

class Singleton {
    private static final INSTANCE = new Singleton();

    private Singleton(){}

    public Singleton instance(){
        return INSTANCE;
    }
 }

il est rare que vous ayez besoin d'une initialisation paresseuse. Vous devriez toujours commencer avec l'initialisation impatiente et ne passer à l'initialisation paresseuse que si vous voyez des problèmes. À moins que vous n'ayez mesuré et identifié l'instanciation Singleton comme étant le coupable du problème de performance, utilisez simplement l'initialisation eager. C'est plus simple et plus performant.

vous pouvez utiliser l'enum pour sûr, mais personnellement je ne me dérange pas parce que l'avantage par rapport à l'instanciation avide normale est la sécurité (contre l'attaque par réflexion), et la plupart du temps mon code est vulnérable à de telles attaques de toute façon :p

5
répondu Enno Shioji 2010-05-26 12:26:24

Josh Bloch recommande 2 solutions:

1) worser:

class Singleton {

   public static Singleton instance = new Singleton();

   ...
}

2) MEILLEUR:

public enum Singleton {

   INSTANCE;

   ...
}
2
répondu Roman 2010-05-26 11:11:04

vous pouvez utiliser cet extrait de code de wiki

public class Singleton {
   // Private constructor prevents instantiation from other classes
   private Singleton() {}

   /**
    * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
    * or the first access to SingletonHolder.INSTANCE, not before.
    */
   private static class SingletonHolder { 
     private static final Singleton INSTANCE = new Singleton();
   }

   public static Singleton getInstance() {
     return SingletonHolder.INSTANCE;
   }
 }
2
répondu Vinay Lodha 2010-05-26 13:29:40