Ce code résout-il le problème de verrouillage à double vérification en Java?
-
12-11-2019 - |
Question
Ce code résout-il le problème de verrouillage à double vérification en Java?
public class DBAccessService() {
private static DBAccessService INSTANCE;
private DBAccessService() {}
public static DBAccessService getInstance() {
if (INSTANCE != null) {
return INSTANCE;
}
return createInstance();
}
private static synchronized DBAccessService createInstance() {
if (INSTANCE != null) {
return INSTANCE;
}
DBAccessService instance = new DBAccessService();
INSTANCE = instance;
return INSTANCE;
}
}
Il y a 2 aspects pour prêter attention:
getInstance()
est ne pas synchronisé, donc une fois l'instance initialisée, il n'y a aucun coût pour la synchronisationcreateInstance()
est synchronisé
Donc, la question est: ce code a-t-il des problèmes? Est-ce légal et toujours Thread-saa?
La solution
Pour résoudre cette question particulière Concurrence de Java dans la pratique (Écrit par l'équipe qui a essentiellement écrit la bibliothèque java.util.concurrent) recommande le Idiom de classe du support d'initialisation paresseux (Page 348 Dans ma copie, énumérant 16.6, pas 16.7)
@ThreadSafe
public class DBAccessServiceFactory {
private static class ResourceHolder {
public static DBAccessService INSTANCE = new DBAccessService();
}
public static DBAccessService getResource() {
return ResourceHolder.INSTANCE;
}
}
C'est toujours légal et en forme de fil. Je ne suis pas un expert, donc je ne peux pas dire que c'est mieux que votre code. Cependant, étant donné que c'est le modèle recommandé par Doug Lea et Joshua Bloch, je l'utiliserais toujours sur le code que vous ou moi avons inventé, car il est si facile de faire des erreurs (comme le montre le nombre de mauvaises réponses à cette question ).
Liés à la question volatile qu'ils disent:
Les modifications ultérieures du JMM (Java 5.0 et ultérieurement) ont permis à DCL de fonctionner si la ressource est rendue volatile ... mais l'idiome du support d'initialisation paresseux offre les mêmes avantages et est plus facile à comprendre.
Autres conseils
Vous devez déclarer INSTANCE
comme volatile
Pour que cela fonctionne:
private static volatile DBAccessService INSTANCE;
Remarque Cela ne fonctionne qu'avec Java 5 et plus tard. Voir La déclaration "le verrouillage à double vérification est brisé".
Dans cet article On prétend que la "journalisation à double vérification" n'est pas un problème si vous utilisez une classe singleton séparée:
public class DBAccessHelperSingleton {
public static DBAccessHelper instance = new DBAccessHelper();
}
Il a le même avantage: le champ n'est pas instancié avant d'être références la première fois.
Comme indiqué précédemment, si vous voulez garder comme vous l'avez volatile
est manquant au cas où vous ne ciblez que JDK> = 5.
Il n'est pas sûr de fil, veuillez vérifier un excellent article. Il n'y a aucune garantie, qu'un thread verra une instance entièrement initialisée de DBAccessService. Pourquoi ne pas utiliser simple
public class DBAccessService() {
public static DBAccessService INSTANCE = new DBAccessService();
}
Semble bien. Si deux threads appellent getInstance () et que l'instance n'est pas initalisée, un seul thread pourra procéder à CreateInstance () et le second verra déjà que l'instance n'est pas nul.
La seule chose est volatile
mot-clé par exemple. Sinon, Java peut le mettre en cache.