Темабезопасный одноэлементный класс
Я написал ниже класс 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
}
}