【问题标题】:Potential problems in Double Checked locking pattern in Singleton classSingleton 类中双重检查锁定模式的潜在问题
【发布时间】:2013-04-19 07:17:19
【问题描述】:

我相信我写的下面的单例类是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 类的任何示例都将帮助我更好地理解如何编写更好的线程安全的 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;
        }
}

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

【问题讨论】:

  • 1) 创建类final 2) 创建静态实例final 3) 覆盖readResolve() 以返回实例 4) 为了完美,从clone() 抛出异常。 5)synchronizegetInstance()

标签: java singleton double-checked-locking


【解决方案1】:

我非常喜欢以下惰性初始化单例模式:

public final class CassandraAstyanaxConnection {

   ...

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

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

   ...
}

【讨论】:

  • ,感谢 Keppil 的建议。我用最新的代码更新了我的问题。你能看一下,让我知道它是否适合你吗?感谢您的帮助。
  • 你能看看我的做法是否正确吗?
  • @FarhanJamal:getInstance() 方法现在看起来不错,该实现没有并发问题。如果你想让它成为真正的Singleton,你需要遵循@NoobUnChained 大纲的建议。
  • @FarhanJamal:遵循建议,除了getInstance() 的同步,此解决方案不需要。
【解决方案2】:

您的 DCL 已损坏,_instance 必须是 volatile 才能正常工作

private static volatile CassandraAstyanaxConnection _instance;

在你的情况下,最好的解决方案是

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

因为 getInstance() 是唯一的公共方法,所以它是惰性的。只有当您调用 getInstance() 类时才会加载并创建实例。这就是 Holder 成语的含义。在您的情况下,您不需要它。整个班级都像那个习语一样工作

【讨论】:

    【解决方案3】:

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

    public enum CassandraAstyanaxConnection {
    
           INSTANCE;
           //fields
    
            public void initializeConnection() {
                //Move work from private constructor here.
            }
    
    
    }
    

    我想这样的东西应该可以工作。

    【讨论】:

    • 任何示例都将基于我的 Singleton 课程而受到赞赏。这样我可以理解更多,也能学到一些东西。
    【解决方案4】:

    如果要对静态字段进行惰性初始化,最好的模式是“使用持有者类进行惰性初始化”:

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

    在这种情况下,延迟初始化由 JVM 保证,直到 ConnectionHolder 类被加载后,该字段才被初始化。

    【讨论】:

      【解决方案5】:

      一个好的单身人士:

      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();访问它

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-04-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-08
        • 1970-01-01
        相关资源
        最近更新 更多