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:

  1. getInstance() est ne pas synchronisé, donc une fois l'instance initialisée, il n'y a aucun coût pour la synchronisation
  2. createInstance() est synchronisé

Donc, la question est: ce code a-t-il des problèmes? Est-ce légal et toujours Thread-saa?

Était-ce utile?

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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top