线程安全单例类

ars*_*nal 58 java singleton multithreading

我写了一个下面的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

谁能帮我这个?对我上面的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

任何人都可以看看,让我知道,如果这次我做对了吗?

谢谢您的帮助.

Boh*_*ian 204

您正在实现延迟初始化模式 - 首次使用时创建实例.

但是有一个简单的技巧,可以让你的代码,一个线程的实现并不需要同步!它被称为按需初始化持有者习语,它看起来像这样:

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
}
Run Code Online (Sandbox Code Playgroud)

这段代码在第一次调用时初始化实例getInstance(),并且由于类加载器的契约,重要的是不需要同步:

  • 类加载器在首次访问时加载类(在这种情况下Holder,只有访问权限在getInstance()方法中)
  • 当一个类被加载,并且在任何人都可以使用它之前,所有静态初始化器都保证被执行(那就是当Holder静态块被激活时)
  • 类加载器有自己的同步内置,使上述两点保证是线程安全的

这是我在需要延迟初始化时使用的一个巧妙的小技巧.你也可以获得一个final实例的奖励,即使它是懒惰的.还要注意代码的清晰和简单.

编辑:您应该将所有构造函数设置为私有或受保护.设置和清空私有构造函数将完成工作

  • 为什么在调用getInstance之前访问您的类?有其他静态方法吗? (2认同)
  • @tgkprog我没有拼写出来(但我已经编辑过了).我正在谈论的初始化类是静态内部类`Holder`,而不是外部类`CassandraAstyanaxConnection`.除了从`getInstance()`中删除`synchronized`关键字之外,此实现不会更改API,从而提高了对实例的访问性能. (2认同)
  • 你的代码是完美的:)是的,我相信这种模式是我们线程安全的最干净,最快的单例模式.欢迎您的帮助.祝您的IT旅程顺利. (2认同)
  • @Bohemian 不一定,如果我传递的内容是我的应用程序(Android)的上下文怎么办 (2认同)
  • @bstar55 如果另一个调用使用不同的参数怎么办?如果仅打算传入一个参数,则创建另一种方法,例如“静态同步初始化(MyParam x)”,该方法只能调用一次,并让“getInstance()”返回在“initialize()”中创建的实例 (2认同)
  • @ w35l3y恕我直言,这是一个误报,应该配置工具或标记的行,以忽略该规则的实例.调用构造函数的类是一个私有内部类,它是类本身的*部分*.你可以使用工厂方法做一些工作:在`Holder`中`INSTANCE = MyClass.create();`并添加`private static MyClass create(){return new MyClass();}`,但这样做会敷衍了事; 完成以回避任意样式规则但不添加任何实际值.最好忽略它. (2认同)

Moh*_*nan 16

以上所有方法都在急切地初始化对象.这个怎么样.这将帮助您懒惰地初始化您的类.您可能有重物,并且您不希望在启动时初始化.

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;

}

} 
Run Code Online (Sandbox Code Playgroud)

  • @bcoughlan volatile关键字将保持安全:)你共享的链接只是通过最后一节.其中一个解决方案是使ur singleton变量volatile. (3认同)
  • 我不确定本文是否仍然适用,但据此已损坏:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html (2认同)
  • 这个实现有一个公共构造函数,所以任何人都可以创建任意数量的对象,因此不能将其视为单例.您让默认的公共构造函数转义 (2认同)