静态方法和变量:
public class Singleton{
private static Singleton singleton = null;
private Singleton(){
}
public static synchronized Singleton getInstance(){
if(singletion == null)
singleton = new Singletion();
return singleton;
}
}
Java 1.5之后的第二个
public class Singleton{
private static volatile Singleton singleton = null;
private Singleton(){
}
public static Singleton getInstance(){
if(singleton == null){
synchronized(this){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
那么这两个线程安全代码的优缺点是什么?我们何时应该使用哪个?
答案 0 :(得分:4)
第二个是线程安全的,但以下更简单,更快,因为它不需要同步块。
public enum Singleton {
INSTANCE;
// define fields and methods here.
}
访问
Singleton.INSTANCE.method();
注意:如果需要,枚举可以实现接口。
答案 1 :(得分:2)
这两种实现都是线程安全且有效的。
前者较短。它更具可读性和可维护性。
后者更快,而且速度也比大多数人想象的要快。根据Java VM的实现,读取 volatile 变量与在x86上读取非易失性变量一样快。这意味着开销仅在初始化期间发生。但是,只要单例被初始化,就没有任何开销。
如果性能真的是一个问题,你应该使用后者。否则使用前者,因为对可读性和可维护性的需求经常被低估。
答案 2 :(得分:0)
第二个'双重检查'单例更快,因为无论何时请求(尽管第一次调用),它都不需要获取锁定。无论如何,控制对象生命周期的最佳方法是使用依赖注入。
答案 3 :(得分:0)
执行线程安全单例的最安全方法是通过initialisation on demand holder idiom:
public class Foo {
private Foo() {}
private static class Holder {
private static final Foo INSTANCE = new Foo();
}
public static Foo getInstance() {
return Holder.INSTANCE;
}
}
这适用于没有正确支持volatile
关键字的Java版本。关于这种模式的巧妙之处在于它仅在getInstance
的第一次访问时使用隐式锁定。之后,访问不同步。这是由于Java中存储管理和静态加载的一个有趣的小怪癖。
答案 4 :(得分:0)
两个例子都是单例的懒惰初始化,即你在需要的时候初始化单例。第二个例子是double checked locking的一个例子,主要针对线程安全。
答案 5 :(得分:-1)
第一个不是线程安全的。如果getInstance
为synchronized
,那么它将是线程安全的。
如果您需要线程安全,请使用第二个。
虽然有一个更为简单的第三种方式,但略有不同(Singleton
在不同的时间创建):
public class Singleton{
private static final Singleton singleton = new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return singleton;
}
}