web-dev-qa-db-fra.com

Classe singleton Thread Safe

J'ai écrit une classe Singleton ci-dessous. Je ne suis pas sûr qu'il s'agisse d'une classe de singleton sans danger pour les fils?

public class CassandraAstyanaxConnection {

    private static CassandraAstyanaxConnection _instance;
    private AstyanaxContext<Keyspace> context;
    private Keyspace keyspace;
    private ColumnFamily<String, String> emp_cf;



    public static synchronized CassandraAstyanaxConnection getInstance() {
        if (_instance == null) {
            _instance = new CassandraAstyanaxConnection();
        }
        return _instance;
    }

    /**
     * Creating Cassandra connection using Astyanax client
     *
     */
    private CassandraAstyanaxConnection() {

        context = new AstyanaxContext.Builder()
        .forCluster(ModelConstants.CLUSTER)
        .forKeyspace(ModelConstants.KEYSPACE)
        .withAstyanaxConfiguration(new AstyanaxConfigurationImpl()      
            .setDiscoveryType(NodeDiscoveryType.RING_DESCRIBE)
        )
        .withConnectionPoolConfiguration(new ConnectionPoolConfigurationImpl("MyConnectionPool")
            .setPort(9160)
            .setMaxConnsPerHost(1)
            .setSeeds("127.0.0.1:9160")
        )
        .withAstyanaxConfiguration(new AstyanaxConfigurationImpl()      
            .setCqlVersion("3.0.0")
            .setTargetCassandraVersion("1.2"))
        .withConnectionPoolMonitor(new CountingConnectionPoolMonitor())
        .buildKeyspace(ThriftFamilyFactory.getInstance());

        context.start();
        keyspace = context.getEntity();

        emp_cf = ColumnFamily.newColumnFamily(
            ModelConstants.COLUMN_FAMILY, 
            StringSerializer.get(), 
            StringSerializer.get());
    }

    /**
     * returns the keyspace
     * 
     * @return
     */
    public Keyspace getKeyspace() {
        return keyspace;
    }

    public ColumnFamily<String, String> getEmp_cf() {
        return emp_cf;
    }
}

Est-ce que quelqu'un peut m'aider avec ça? Toute pensée sur mon cours ci-dessus Singleton sera d'une grande aide.

Code mis à jour: -

J'essaie d'intégrer la suggestion bohémienne dans mon code. Voici le code mis à jour, j'ai

public class CassandraAstyanaxConnection {
    private static class ConnectionHolder {
        static final CassandraAstyanaxConnection connection = new CassandraAstyanaxConnection();
    }
    public static CassandraAstyanaxConnection getInstance() {
        return ConnectionHolder.connection;
    }
    /**
     * Creating Cassandra connection using Astyanax client
     *
     */
    private CassandraAstyanaxConnection() {
        context = new AstyanaxContext.Builder()
        .forCluster(ModelConstants.CLUSTER)
        .forKeyspace(ModelConstants.KEYSPACE)
        .withAstyanaxConfiguration(new AstyanaxConfigurationImpl()      
        .setDiscoveryType(NodeDiscoveryType.RING_DESCRIBE)
                )
                .withConnectionPoolConfiguration(new ConnectionPoolConfigurationImpl("MyConnectionPool")
                .setPort(9160)
                .setMaxConnsPerHost(1)
                .setSeeds("127.0.0.1:9160")
                        )
                        .withAstyanaxConfiguration(new AstyanaxConfigurationImpl()      
                        .setCqlVersion("3.0.0")
                        .setTargetCassandraVersion("1.2"))
                        .withConnectionPoolMonitor(new CountingConnectionPoolMonitor())
                        .buildKeyspace(ThriftFamilyFactory.getInstance());
        context.start();
        keyspace = context.getEntity();
        emp_cf = ColumnFamily.newColumnFamily(
                ModelConstants.COLUMN_FAMILY, 
                StringSerializer.get(), 
                StringSerializer.get());
    }
    /**
     * returns the keyspace
     * 
     * @return
     */
    public Keyspace getKeyspace() {
        return keyspace;
    }
    public ColumnFamily<String, String> getEmp_cf() {
        return emp_cf;
    }
}

Quelqu'un peut-il jeter un coup d'œil et me laisser savoir si cette fois, j'ai bien compris ou non?

Merci pour l'aide.

51
arsenal

Vous implémentez le modèle initialisation différée - où l'instance est créée lors de sa première utilisation.

Mais il existe une astuce simple qui vous permet de coder une implémentation threadsafe qui ne nécessite pas nécessite une synchronisation! Il est connu sous le nom de idiome de titulaire d'initialisation à la demande} , et ressemble à ceci:

public class CassandraAstyanaxConnection {

    private CassandraAstyanaxConnection(){ }        

    private static class Holder {
       private static final CassandraAstyanaxConnection INSTANCE = new CassandraAstyanaxConnection();
    }

    public static CassandraAstyanaxConnection getInstance() {
        return Holder.INSTANCE;
    }
    // rest of class omitted
}

Ce code initialise l'instance lors du premier appel de getInstance() et n'a surtout pas besoin de synchronisation en raison du contrat du chargeur de classe:

  • le chargeur de classes charge les classes lors de leur premier accès (dans ce cas, l'accès unique à Holder est compris dans la méthode getInstance())
  • quand une classe est chargée, et avant que quiconque puisse l'utiliser, tous les initialiseurs statiques sont garantis pour être exécutés (c'est le moment où le bloc statique de Holder se déclenche)
  • le chargeur de classe a sa propre synchronisation intégrée, qui garantit la sécurité des threads

C'est un petit truc sympa que j'utilise chaque fois que j'ai besoin d'une initialisation lente. Vous bénéficiez également du bonus d'une instance final, même si elle est créée paresseusement. Notez également à quel point le code est propre et simple.

Edit: Vous devez définir tous les constructeurs comme privés ou protégés. Le paramétrage et le constructeur privé vide feront le travail

189
Bohemian

toutes les méthodes ci-dessus initialisent avec impatience l'objet. que dis-tu de ça. Cela vous aidera à initialiser votre classe paresseusement. Vous pouvez avoir un objet lourd et vous ne voulez pas initialiser au démarrage.

public class MySinglton { 

  private MySinglton (){}

  private static volatile MySinglton s;

  public static MySinglton getInstance(){

   if (s != null ) return s;

    synchronized(MySinglton.class){

     if (s == null ) {

      s = new MySinglton();
     }
  }

  return s;

}

} 
15
Mohammad Adnan

Non, ce n'est pas thread-safe si les valeurs retournées sur les méthodes pulbic sont des objets changeables.

Pour cette classe être Thread-safe, une manière est de la rendre immuable.

Pour ce faire, vous pouvez changer cette méthode de la manière suivante:

public Keyspace getKeyspace() {
    // make a copy to prevent external user to modified or ensure that Keyspace is immutable, in that case, you don't have to make a copy
    return new Keyspace( keyspace );
}

public ColumnFamily<String, String> getEmp_cf() {
    // Same principle here. If ColumnFamily is immutable, you don't have to make a copy. If its not, then make a copy
    return new ColumnFamily( emp_cf );
}

Dans ce livre Java Concurrency in Practice vous pouvez voir le principe de cette immutabilité.

1
Jose Renato

Comme mentionné dans cet excellent article ici :

Extrait de code

public class Singelton {

    private static final Singelton singleObject = new Singelton();

    public Singelton getInstance(){
        return singleObject;
    }
}
1
Andro Secy

Après la version Java 1.5, nous pouvons utiliser volatile. Si nous avons utilisé le service de garde de clés Java volatile, nous pouvons créer une classe singlton avec thread safe, car les variables d'instance partagent également avec les autres threads. 

public class SingleWithThreadSafe {

    // create an object static referance of SingleWithThreadSafe with volatile
    private static volatile SingleWithThreadSafe instance = null;

    // if we make the constructor private so that this class cannot be
    // instantiated from out side of class
    private SingleWithThreadSafe() {
    }

    // Get only object available
    public static SingleWithThreadSafe getInstance() {
        if (instance == null) {
            instance = new SingleWithThreadSafe();
        }
        return instance;
    }

    public void showMessage() {
        System.out.println("Hello World!");
    }
}
0
Thusitha Indunil

Non, cela ne semble pas être thread-safe. Il semble que des données mutables soient accessibles après l'appel de getInstance, où le verrou aurait été libéré.

0