Singleton类中双重检查锁定模式的潜在问题

时间:2013-04-19 07:17:19

标签: java singleton double-checked-locking

我相信我写的下面的Singleton类是Thread Safe

双重检查的锁定模式在某些情况下可能会遇到问题(我已经看到人们发出警告,虽然它是在不久之前所以我现在只是谷歌搜索答案)

我现在不确定,我的Singleton类下面的Double Checked锁定模式是否会有任何问题。我添加了双重检查锁定模式,使程序运行得更快。

    public class CassandraAstyanaxConnection {

        private static CassandraAstyanaxConnection _instance;
        private static final Object syncObject = new Object();
        private AstyanaxContext<Keyspace> context;
        private Keyspace keyspace;
        private ColumnFamily<String, String> emp_cf;


       public static CassandraAstyanaxConnection getInstance() {
            if (_instance == null) {
                     synchronized(syncObject) {
                        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类中是否有双重检查锁定模式的任何问题?

创建Singleton类线程安全的最佳方法是什么。那个Holder Class Idiom怎么样?我可以在我的Singleton类中使用它吗?

我上面的Singleton类的任何示例基础都将帮助我更好地理解如何编写更好的线程安全单例类。

感谢您的帮助。

更新代码: -

经过一些建议后,我对代码进行了更改 -

public class CassandraAstyanaxConnection {

   private static class ConnectionHolder {
       public static 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;
        }
}

如果这看起来正确,请告诉我。

5 个答案:

答案 0 :(得分:2)

对于懒惰初始化的单身人士,我是以下模式的忠实粉丝:

public final class CassandraAstyanaxConnection {

   ...

   private static class ConnectionHolder {
       public static CassandraAstyanaxConnection connection = new CassandraAstyanaxConnection()
   }

   public static CassandraAstyanaxConnection getInstance() {
        return ConnectionHolder.connection;
   }

   ...
}

答案 1 :(得分:1)

创建线程安全单例类的最佳方法是使用枚举

public enum CassandraAstyanaxConnection {

       INSTANCE;
       //fields

        public void initializeConnection() {
            //Move work from private constructor here.
        }


}

我认为这样的事情应该有用。

答案 2 :(得分:1)

你的DCL坏了,_instance必须是易失性才能正常工作

private static volatile CassandraAstyanaxConnection _instance;

在您的情况下,最佳解决方案是

private static CassandraAstyanaxConnection _instance = new CassandraAstyanaxConnection();
...
public static CassandraAstyanaxConnection getInstance() {
    return _instance;
}

因为getInstance()是唯一的惰性公共方法。只有在调用getInstance()时才会加载类并创建实例。这就是霍尔德成语的意思。在你的情况下,你不需要它。整个班级就像那个成语一样

答案 3 :(得分:0)

一个好的单身人士:

1-制作课程final

2-制作static实例final

3-覆盖readResolve()以返回实例。

4-完美,从clone()抛出异常。

5-同步getInstance()

6-制作默认构造函数private

按照this查看双重检查锁定习惯是否真的有效。

最好的方法是使用enum。这种东西:

public enum DBConn{
 CON_INSTANCE;
 private CassandraAstyanaxConnection connection;
 public synchronized CassandraAstyanaxConnection getConnection(){
   if(connection == null){
      // instantiate a connection
   }
   return connection;
 }
}

通过DBConn.CON_INSTANCE.getConnection();

访问它

答案 4 :(得分:0)

如果你想在静态字段上进行延迟初始化,最好的模式是“使用持有者类进行延迟初始化”:

public class CassandraAstyanaxConnection {
    private static class ConnectionHolder {
       static final CassandraAstyanaxConnection field = 
              new CassandraAstyanaxConnection();
    }

    public static CassandraAstyanaxConnection getInstance() { 
         return ConnectionHolder.field; 
    }
...
}

在这种情况下,JVM保证延迟初始化,在ConnectionHolder类加载类之前,字段不会被初始化。