Темабезопасный одноэлементный класс

Я написал ниже класс Singleton. Я не уверен, является ли это потокобезопасным одноэлементным классом или нет?

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

Может ли кто-нибудь помочь мне с этим? Любые мысли о моем выше классе Singleton будут очень полезны.

Обновленный код: -

Я пытаюсь включить в свой код богемское предложение. Вот обновленный код, я получил -

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

Может ли кто-нибудь взглянуть и сообщить мне, на этот раз я правильно понял или нет?

Спасибо за помощь.

Ответ 1

Вы реализуете шаблон lazy initialization - где экземпляр создается при первом использовании.

Но есть простой трюк, который позволяет вам закодировать поточную реализацию, которая не требует синхронизации! Он известен как Инициализация по требованию владельца идиомы и выглядит следующим образом:

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
}

Этот код инициализирует экземпляр при первом вызове getInstance() и, что важно, не требует синхронизации из-за контракта загрузчика классов:

  • загрузчик классов загружает классы при первом доступе (в этом случае Holder только доступ находится в методе getInstance())
  • когда класс загружается, и до того, как кто-либо сможет его использовать, гарантируются выполнение всех статических инициализаторов (что при статическом статическом Holder)
  • у загрузчика классов есть своя собственная синхронизация, построенная справа, в результате чего указанные выше две точки гарантируются как потокобезопасные

Это аккуратный маленький трюк, который я использую всякий раз, когда мне нужна ленивая инициализация. Вы также получаете бонус экземпляра final, хотя он создан лениво. Также обратите внимание, насколько чистый и простой код.

Изменить: Вы должны установить все конструкторы как частные или защищенные. Установка и пустой частный конструктор будет выполнять работу

Ответ 2

все вышеперечисленные методы охотно инициализируют объект. как насчет этого. Это поможет вам лениво инициализировать ваш класс. У вас может быть тяжелый объект, и вы не хотите инициализироваться при запуске.

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;

}

} 

Ответ 3

Нет, он не поточно-ориентированный, если значения, возвращаемые в методах pulbic, являются изменяемыми объектами.

Чтобы этот класс был потокобезопасным, можно изменить его на неизменяемый.

Чтобы сделать это, вы можете изменить эти методы следующим образом:

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

В этой книге Java Concurrency in Practice вы можете увидеть принцип этой неизменности.

Ответ 4

Как упоминается в этой замечательной статье здесь:

Лучшим решением этой проблемы является [...] использование статического поля

public class Singelton {

    private static final Singelton singleObject = new Singelton();

    public Singelton getInstance(){
        return singleObject;
    }
}

Ответ 5

Нет, это не похоже на потокобезопасность. Похоже, что вы можете изменить данные, доступные после вызова getInstance, где блокировка будет выпущена.

Ответ 6

После версии Java 1.5 мы можем использовать volatile. Если бы мы использовали изменяемый ключ Java, мы могли бы создать класс singlton с безопасным потоком, потому что переменная экземпляра также делится с другим потоком.

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

Ответ 7

Я думаю, что это будет делать то же самое без необходимости проверять каждый раз. static - это то же самое, что и проверка в первый раз

public class Singl {        
    private static Singl _instance;
    //other vars        
    static{
            //synchronized(Singl.class){//do not need
                    _instance = new Singl();
            //}
    }

     public static Singl getInstance() {
             return _instance;

     }

     private Singl(){
             //initizlize
     }

}