private volatile static Singleton uniqueInstance
Dans un singleton lors de l'utilisation de la méthode de double verrouillage pour la synchronisation, pourquoi l'instance unique est-elle déclarée volatile? Puis-je obtenir la même fonctionnalité sans la déclarer volatile?
Sans volatile
, le code ne fonctionne pas correctement avec plusieurs threads.
De Wikipédia Verrouillage vérifié :
Depuis J2SE 5.0, ce problème a été corrigé. Le mot clé volatile garantit désormais que plusieurs threads gèrent correctement l'instance singleton. Ce nouvel idiome est décrit dans La déclaration "Le verrouillage à double vérification est rompu" :
// Works with acquire/release semantics for volatile
// Broken under Java 1.4 and earlier semantics for volatile
class Foo {
private volatile Helper helper = null;
public Helper getHelper() {
Helper result = helper;
if (result == null) {
synchronized(this) {
result = helper;
if (result == null) {
helper = result = new Helper();
}
}
}
return result;
}
// other functions and members...
}
En général, vous devriez éviter le double contrôle du verrouillage si possible, car il est difficile de corriger et si vous vous trompez, il peut être difficile de trouver l'erreur. Essayez plutôt cette approche plus simple:
Si l'objet d'assistance est statique (un par chargeur de classe), une alternative est le initialisation sur l'idiome du titulaire de la demande
// Correct lazy initialization in Java
@ThreadSafe
class Foo {
private static class HelperHolder {
public static Helper helper = new Helper();
}
public static Helper getHelper() {
return HelperHolder.helper;
}
}
volatile
empêche la réécriture des écritures en mémoire, ce qui empêche les autres threads de lire les champs non initialisés de votre singleton via le pointeur du singleton.
Considérez cette situation: le thread A découvre que uniqueInstance == null
, verrouille, confirme qu'il s'agit toujours de null
et appelle le constructeur de singleton. Le constructeur effectue une écriture dans le membre XYZ
à l'intérieur de Singleton et retourne. Le thread A écrit maintenant la référence au singleton nouvellement créé dans uniqueInstance
et se prépare à libérer son verrou.
Tout comme le thread A s'apprête à libérer son verrou, le thread B arrive et découvre que uniqueInstance
n'est pas null
. Le fil B
accède uniqueInstance.XYZ
pensant qu'il a été initialisé, mais parce que le CPU a réorganisé les écritures, les données que le thread A a écrites dans XYZ
n'ont pas été rendues visibles au thread B. Par conséquent, le thread B voit une valeur incorrecte à l'intérieur XYZ
, ce qui est faux.
Lorsque vous marquez uniqueInstance
volatile, une barrière de mémoire est insérée. Toutes les écritures initiées avant celle de uniqueInstance
seront terminées avant la modification de uniqueInstance
, empêchant la situation de réorganisation décrite ci-dessus.
Pour éviter d'utiliser un double verrouillage, ou volatile j'utilise ce qui suit
enum Singleton {
INSTANCE;
}
La création de l'instance est simple, chargée paresseusement et sûre pour les threads.
L'écriture dans un champ volatile se produira avant toute opération de lecture. Voici un exemple de code pour une meilleure compréhension:
private static volatile ResourceService resourceInstance;
//lazy Initialiaztion
public static ResourceService getInstance () {
if (resourceInstance == null) { // first check
synchronized(ResourceService.class) {
if (resourceInstance == null) { // double check
// creating instance of ResourceService for only one time
resourceInstance = new ResourceService ();
}
}
}
return resourceInstance;
}
Ce lien peut mieux vous servir http://javarevisited.blogspot.com/2011/06/volatile-keyword-Java-example-tutorial.html