it-swarm.com.de

Sichere Singleton-Klasse für Threads

Ich habe eine Singleton-Klasse geschrieben. Ich bin mir nicht sicher, ob es sich um eine Thread-sichere Singleton-Klasse handelt oder nicht?

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;
    }
}

Kann mir jemand dabei helfen? Alle Gedanken zu meiner obigen Singleton-Klasse sind eine große Hilfe.

Aktualisierter Code: -

Ich versuche, den böhmischen Vorschlag in meinen Code aufzunehmen. Hier ist der aktualisierte Code, den ich

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;
    }
}

Kann jemand einen Blick darauf werfen und mich wissen lassen, ob ich es diesmal richtig verstanden habe oder nicht?

Danke für die Hilfe.

51
arsenal

Sie implementieren das Lazy-Initialisierung pattern - wobei die Instanz bei der ersten Verwendung erstellt wird.

Es gibt jedoch einen einfachen Trick, mit dem Sie eine threadsichere Implementierung codieren können, bei der nicht eine Synchronisierung erfordert! Es ist bekannt als Initialisierung auf Abrufinhaber-Idiom und sieht folgendermaßen aus:

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
}

Dieser Code initialisiert die Instanz beim ersten Aufruf von getInstance() und benötigt aufgrund des Vertrags des Klassenladeprogramms keine Synchronisierung:

  • der Klassenlader lädt Klassen beim ersten Zugriff (in diesem Fall liegt der einzige Zugriff auf Holder innerhalb der getInstance()-Methode)
  • wenn eine Klasse geladen ist und bevor sie verwendet werden kann, werden garantiert alle statischen Initialisierer ausgeführt (dann wird der statische Block von Holder ausgelöst)
  • der Klassenlader hat eine eigene Synchronisation, die die beiden oben genannten Punkte garantiert threadsicher macht

Es ist ein netter kleiner Trick, den ich immer dann benutze, wenn ich eine langsame Initialisierung brauche. Sie erhalten auch den Bonus einer final-Instanz, auch wenn sie nur faul erstellt wurde. Beachten Sie auch, wie sauber und einfach der Code ist.

Edit: Sie sollten alle Konstruktoren als privat oder geschützt festlegen. Das Festlegen und Leeren eines privaten Konstruktors erledigt die Arbeit

189
Bohemian

alle obigen Methoden initialisieren eifrig das Objekt. Wie wäre es damit. Dies hilft Ihnen, Ihre Klasse faul zu initialisieren. Sie haben möglicherweise ein schweres Objekt und möchten beim Start nicht initialisieren.

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

Nein, es ist nicht fadensicher, wenn die von den Pulbic-Methoden zurückgegebenen Werte zufällige Objekte sind.

Um für diese Klasse Thread-sicher zu sein, besteht eine Möglichkeit darin, sie in unveränderlich zu ändern.

Um dies zu tun, können Sie diese Methoden folgendermaßen ändern:

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 );
}

In diesem Buch Java Concurrency in Practice sehen Sie das Prinzip dieser Unveränderlichkeit.

1
Jose Renato

Wie in diesem tollen Artikel hier erwähnt:

Code-Auszug

public class Singelton {

    private static final Singelton singleObject = new Singelton();

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

Nach der Java 1.5-Version können wir volatile verwenden. Wenn Sie volatile Java-Schlüsselstation verwendet haben, können Sie eine Singlton-Klasse mit threadsicherer, weil Instanzvariable gemeinsam mit anderem Thread erstellen. 

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

Nein, scheint nicht threadsicher zu sein. Es scheint, dass Sie nach dem Aufruf von getInstance auf veränderliche Daten zugreifen können, bei denen die Sperre freigegeben worden wäre.