69

我写了一个下面的单例类。我不确定这是否是线程安全的单例类?

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

谁能看一下,让我知道这次我做对了吗?

谢谢您的帮助。

4

8 回答 8

231

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

但是有一个简单的技巧可以让你编写一个不需要同步的线程安全实现!它被称为Initialization-on-demand holder idiom,它看起来像这样:

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's 的静态块触发时)
  • 类加载器有自己的内置同步功能,可以保证上述两点是线程安全的

这是我在需要延迟初始化时使用的一个巧妙的小技巧。final即使它是惰性创建的,您也可以获得实例的奖励。还要注意代码是多么干净和简单。

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

于 2013-04-19T14:08:32.083 回答
22

以上所有方法都在热切地初始化对象。这个怎么样。这将帮助你懒惰地初始化你的类。您可能有很重的物体,并且您不想在启动时进行初始化。

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;

}

} 
于 2013-09-13T11:37:28.950 回答
2

不,如果公共方法上返回的值是可更改的对象,则它不是线程安全的。

使此类成为线程安全的一种方法是将其更改为不可变的。

为此,您可以像这样更改此方法:

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 中,您可以看到不变性的原理。

于 2013-04-19T14:19:13.330 回答
2

正如这篇伟大的文章中提到

这个问题的最佳解决方案是 [...] 使用静态字段

public class Singelton {

    private static final Singelton singleObject = new Singelton();

    public Singelton getInstance(){
        return singleObject;
    }
}
于 2017-02-02T12:16:36.190 回答
1

不,这似乎不是线程安全的。在调用 之后,您似乎可以访问可变数据getInstance,其中锁定将被释放。

于 2013-04-19T13:56:21.683 回答
0

这应该是使用双重检查锁定原理实现单例模式的正确方法:

class MySinglton { 

    private static volatile MySinglton instance;
    
    private MySinglton() {}

    public static MySinglton getInstance() {
        if (instance == null) {
            synchronized (MySinglton.class) {
                if (instance == null) {
                    instance = new MySinglton();
                }
            }
        }
        return instance;
    }

}
于 2021-06-03T20:39:44.603 回答
-1

我认为这会做同样的事情,而不必每次都检查。静态与第一次检查相同

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
     }

}
于 2013-04-19T13:10:29.607 回答
-1

在 java 1.5 版本之后我们可以使用 volatile。如果我们使用 volatile java key ward,我们可以创建线程安全的单例类,因为实例变量也与其他线程共享。

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!");
    }
}
于 2019-01-08T09:43:03.543 回答