web-dev-qa-db-fra.com

Double contrôle de verrouillage à Singleton

voici ma classe personnalisée pour le motif singleton. Dans ce code, j'utilise un double contrôle de verrouillage comme ci-dessous. En lisant de nombreux articles sur certaines sources, ils disent que le double contrôle est utile car il empêche deux threads simultanés de s'exécuter simultanément pour créer deux objets différents.

public class DoubleCheckLocking {

    public static class SearchBox {
        private static volatile SearchBox searchBox;

        // private constructor
        private SearchBox() {}

        // static method to get instance
        public static SearchBox getInstance() {
            if (searchBox == null) { // first time lock
                synchronized (SearchBox.class) {
                    if (searchBox == null) {  // second time lock
                        searchBox = new SearchBox();
                    }
                }
            }
            return searchBox;
        }
}

Je ne comprends toujours pas le code ci-dessus. Quel est le problème, si deux threads exécutent ensemble la même ligne de code lorsque l'instance est null?

if (searchBox == null) {
                synchronized (SearchBox.class) {
                    if (searchBox == null) {
                        searchBox = new SearchBox();
                    }
                }
            }

Quand cela apparaît. les deux threads verront que l'objet est null. alors les deux se synchronisent. et ensuite, ils vérifient à nouveau, et le voient toujours nul. et créer deux objets différents. OOOPS.

S'il vous plaît expliquer pour moi. Qu'est-ce que j'ai mal compris?

Merci :)

58
hqt

Non, puisque vous obtenez le verrou sur le SearchBox.class, un seul thread entrera dans le bloc synchronisé à la fois. Donc le premier thread entre, puis trouve searchBox est nul et le crée puis quitte le bloc synchronisé, puis le deuxième thread entre dans le bloc puis il constate que le searchBox n'est pas nul car le premier thread déjà créé pour ne pas créer une nouvelle instance de searchBox.

Le modèle à double vérification est utilisé pour éviter d’obtenir le verrouillage chaque fois que le code est exécuté. Si l'appel ne se produit pas simultanément, la première condition échouera et l'exécution du code n'exécutera pas le verrouillage, économisant ainsi des ressources.

65
Arun P Johny

Regardons ce code:

1 if (searchBox == null) {
2     synchronized (SearchBox.class) {
3     if (searchBox == null) {
4         searchBox = new SearchBox();
5     }
6 }

Essayons de raisonner à ce sujet. Disons que nous avons deux threads A et B et supposons qu'au moins l'un d'entre eux atteigne la ligne 3 et observe searchBox == null est true. Deux threads ne peuvent pas être tous les deux à la ligne 3 en même temps à cause du bloc synchronized. C'est la clé pour comprendre pourquoi le verrouillage à double vérification fonctionne. Donc, il faut le cas que soit A ou B soit passé par synchronized en premier. Sans perte de généralité, disons que ce fil est A. Puis, en voyant searchBox == null est vrai, il entrera dans le corps de la déclaration et affectera searchBox à une nouvelle instance de SearchBox. Il finira par quitter le bloc synchronized. Maintenant, ce sera au tour de B d'entrer: rappelez-vous, B a été bloqué dans l'attente de la sortie de A. Maintenant, quand il entrera dans le bloc, il observera searchBox. Mais A n'aura quitté que le fait d'avoir défini searchBox sur une valeur autre que null. Terminé.

En passant, en Java, le meilleur moyen d'implémenter un singleton est d'utiliser un type enum à un seul élément. De Effective Java :

Bien que cette approche n’ait pas encore été adoptée à grande échelle, un type d’énumération à un seul élément est le meilleur moyen de mettre en œuvre un singleton.

25
jason

Ce double verrouillage n’est nécessaire que si vous êtes inquiet au sujet de nombreux threads appelant le singleton simultanément ou du coût d’obtention d’un verrou en général.

Son but est d'éviter toute synchronisation inutile, afin de garder votre code rapide dans un environnement multithread.

Consultez ce lien pour plus d'informations.

Si vous utilisez Java 1,5 ou plus, et que vous utilisez le mot-clé volatile dans votre mécanisme de double vérification verrouillé, tout fonctionnera correctement. Si vous utilisez le volatile mot-clé, votre exemple n'est pas cassé selon le même lien que ci-dessus.

10
William Morrison
if (searchBox == null) { //1
    synchronized (SearchBox.class) {
        if (searchBox == null) {  //2
            searchBox = new SearchBox();
            }
        }
    }
}
  1. Si une instance a déjà été créée, ne faites rien - évitez de verrouiller les threads
  2. Le premier thread qui a acquis le verrou vérifie et constate qu'il n'existe aucun objet de ce type et le crée. Il libère le verrou et le second peut faire la même chose - il doit vérifier si l'objet existe car le premier l'a peut-être créé.

Donc, fondamentalement, le if externe est utilisé pour empêcher les verrous redondants - il permet à tous les threads de savoir qu’il existe déjà un objet et qu’ils n’ont pas besoin de verrouiller/de faire quoi que ce soit. Et le if interne est utilisé pour permettre à un thread simultané de savoir si un autre a déjà créé l'objet ou non.

2
stan0