Veuillez clarifier mes questions concernant Singleton et le multithreading:
getInstance()
En même temps?getInstance()
synchronized
de singleton?Oui il faut. Vous pouvez utiliser plusieurs méthodes pour atteindre la sécurité des threads avec une initialisation différée:
Synchronisation draconienne:
private static YourObject instance;
public static synchronized YourObject getInstance() {
if (instance == null) {
instance = new YourObject();
}
return instance;
}
Cette solution nécessite que chaque thread soit synchronisé alors qu'en réalité, seuls les premiers doivent l'être.
Double vérification de la synchronisation :
private static final Object lock = new Object();
private static volatile YourObject instance;
public static YourObject getInstance() {
YourObject r = instance;
if (r == null) {
synchronized (lock) { // While we were waiting for the lock, another
r = instance; // thread may have instantiated the object.
if (r == null) {
r = new YourObject();
instance = r;
}
}
}
return r;
}
Cette solution garantit que seuls les premiers threads qui tentent d’acquérir votre singleton doivent passer par le processus d’acquisition du verrou.
private static class InstanceHolder {
private static final YourObject instance = new YourObject();
}
public static YourObject getInstance() {
return InstanceHolder.instance;
}
Cette solution tire parti des garanties du modèle de mémoire Java concernant l'initialisation de la classe pour assurer la sécurité des threads. Chaque classe ne peut être chargée qu'une seule fois et ne sera chargée que si cela est nécessaire. Cela signifie que la première fois que getInstance
est appelé, InstanceHolder
sera chargé et instance
sera créé. Et comme ceci est contrôlé par ClassLoader
s, aucune synchronisation supplémentaire n'est nécessaire.
Ce modèle effectue une initialisation lazy-thread-safe de l'instance sans synchronisation explicite!
public class MySingleton {
private static class Loader {
static final MySingleton INSTANCE = new MySingleton();
}
private MySingleton () {}
public static MySingleton getInstance() {
return Loader.INSTANCE;
}
}
Cela fonctionne car il utilise le chargeur de classes pour effectuer toute la synchronisation pour vous gratuitement: La classe MySingleton.Loader
est d'abord accédée dans la méthode getInstance()
. La classe Loader
est donc chargée lorsque getInstance()
est appelé pour la première fois. De plus, le chargeur de classe garantit que toutes les initialisations statiques sont terminées avant que vous n’ayez accès à la classe - c’est ce qui vous garantit la sécurité des threads.
C'est comme par magie.
C'est en fait très similaire au modèle enum de Jhurtado, mais je trouve que le modèle enum est un abus du concept enum (bien que cela fonctionne)
Si vous travaillez sur un environnement multithread en Java et que vous devez garantir que tous ces threads accèdent à une seule instance d'une classe, vous pouvez utiliser un Enum. Cela aura l’avantage supplémentaire de vous aider à gérer la sérialisation.
public enum Singleton {
SINGLE;
public void myMethod(){
}
}
et alors juste que vos threads utilisent votre instance comme:
Singleton.SINGLE.myMethod();
Oui, vous devez synchroniser getInstance()
. Si ce n'est pas le cas, il peut arriver que plusieurs instances de la classe puissent être créées.
Prenons le cas où vous avez deux threads qui appellent getInstance()
en même temps. Maintenant, imaginez que T1 s’exécute juste après la vérification instance == null
, puis que T2 s’exécute. À ce stade, l’instance n’est ni créée ni définie. T2 réussira donc la vérification et créera l’instance. Maintenant, imaginez que l'exécution revienne à T1. Maintenant, le singleton est créé, mais T1 a déjà effectué le contrôle! Il va procéder à faire l'objet à nouveau! Rendre getInstance()
synchronisé empêche ce problème.
Il existe plusieurs façons de rendre les singletons thread-safe, mais rendre getInstance()
synchronisé est probablement le plus simple.
Enum singleton
Le moyen le plus simple d'implémenter un Singleton compatible avec les threads est d'utiliser un Enum.
public enum SingletonEnum {
INSTANCE;
public void doSomething(){
System.out.println("This is a singleton");
}
}
Ce code fonctionne depuis l’introduction d’Enum en Java 1.5.
Double vérification du verrouillage
Si vous souhaitez coder un singleton «classique» fonctionnant dans un environnement multithread (à partir de Java 1.5), vous devez utiliser celui-ci.
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class){
if (instance == null) {
instance = new Singleton();
}
}
}
return instance ;
}
}
Ce n'est pas thread-safe avant 1.5 parce que l'implémentation du mot-clé volatile était différente.
Chargement précoce de Singleton (fonctionne même avant Java 1.5)
Cette implémentation instancie le singleton lorsque la classe est chargée et fournit la sécurité du thread.
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
public void doSomething(){
System.out.println("This is a singleton");
}
}
Vous pouvez également utiliser un bloc de code statique pour instancier l'instance au chargement de la classe et éviter les problèmes de synchronisation des threads.
public class MySingleton {
private static final MySingleton instance;
static {
instance = new MySingleton();
}
private MySingleton() {
}
public static MySingleton getInstance() {
return instance;
}
}
Quel est le meilleur moyen d'implémenter Singleton en Java, dans un environnement multithread?
Reportez-vous à cet article pour connaître le meilleur moyen d'implémenter Singleton.
Quel est un moyen efficace d'implémenter un motif singleton en Java?
Que se passe-t-il lorsque plusieurs threads essaient d'accéder à la méthode getInstance () en même temps?
Cela dépend de la façon dont vous avez implémenté la méthode. Si vous utilisez le double verrouillage sans variable volatile, vous pouvez obtenir un objet Singleton partiellement construit.
Référez-vous à cette question pour plus de détails:
Pourquoi l'utilisation de volatile dans cet exemple de verrouillage à double contrôle
Pouvons-nous synchroniser la méthode getInstance () de singleton?
La synchronisation est-elle vraiment nécessaire lors de l'utilisation de classes Singleton?
Non requis si vous implémentez Singleton de différentes manières
Référez-vous à cette question pour plus de détails
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis () {...}
}
Source: Effective Java -> élément 2
Il suggère de l'utiliser si vous êtes sûr que la classe restera toujours singleton.