Java 为什么静态单例是避免';DCL';?
当需要单例时,静态字段是一个优雅的解决方案吗Java 为什么静态单例是避免';DCL';?,java,singleton,dcl,Java,Singleton,Dcl,当需要单例时,静态字段是一个优雅的解决方案吗 class HelperSingleton { static Helper singleton = new Helper(); public static Helper getInstance() { return singleton; } } 当两个线程同时访问getInstance时,字段singleton是否有可能未完全初始化?或者查看帮助对象字段的默认值,而不是构造函数中设置的值? 静态单例也是惰性初始
class HelperSingleton {
static Helper singleton = new Helper();
public static Helper getInstance() {
return singleton;
}
}
当两个线程同时访问getInstance
时,字段singleton
是否有可能未完全初始化?或者查看帮助对象字段的默认值,而不是构造函数中设置的值?
静态单例也是惰性初始化吗
我是说,
static Helper singleton=new Helper()代码>
这是原子弹吗?并且不会返回默认值?请查看
有许多样式可以解释好的/坏的。我认为最优雅的单身解决方案是:
public enum Singleton {
INSTANCE;
public Singleton getInstance() {
return INSTANCE;
}
}
使用单例模式有些问题,因为有时您不知道您实际上只有一个。例如在使用类加载器时考虑该情况。
顺便说一句,问题(和其他问题)有一个彻底的解释。如果是这样的话:
class HelperSingleton {
static Helper singleton = null;
public static Helper getInstance() {
if(singleton == null) {
singleton = new Helper();
}
return singleton;
}
}
这里可能有一种可能性:
线程1调用getInstance()
方法并确定singleton
为null
线程1进入if块,但在创建新实例之前被线程2抢占
线程2调用getInstance()
方法并确定实例为null
线程2进入if块并创建一个新的Helper
对象,并将变量singleton
分配给这个新对象
线程2返回单例对象引用
线程2被线程1抢占
线程1从它停止的地方开始执行singleton=newhelper()代码>将导致创建另一个单例对象
线程1返回这个对象
因此,我们以两个实例结束。创建单例的最佳方法是使用enum
在这里,当类被加载时,Helper
的一个新实例被创建,并且singleton
保存对该实例的引用。您可以在中获得详细的初始化过程。1)当线程访问静态getInstance时,类加载器第一次必须加载HelperSingleton类,它将在加载类之前锁定其他线程。因此存在隐式同步。J.Bloch“有效Java”第71项现代虚拟机将仅同步字段访问以初始化类。初始化类后,VM将修补代码,以便后续对字段的访问不涉及任何测试或同步
2) 您的singleon是懒惰的,因为只有一个访问点—getInstance。不仅实例是按需创建的,整个类也是按需加载的。类将不会被初始化
在使用之前[JLS,12.4.1]。getInstance()不应该是静态的吗?应该有一个私有构造函数。而singleton
应该是私有的和最终的。理想情况下,类也应该是final
。[编辑]dangit@JonSkeet,你偷了我的答案,你比我快:-关于单例类和线程安全,这个问题可能是一个有用的读物。谢谢,但我想知道的是静态帮助程序单例=新帮助程序();始终向客户端返回一个完全初始化的引用?是否有任何关于您提到的第1点的官方文档?我认为J.Bloch的有效Java是可以信任的,因为作者领导了许多Java平台功能的设计和实现。呃,上面的静态字段singleton和lazy initaialization holder类之间有什么区别吗?如果类中有其他静态方法,则需要将实例包装在holder中以保持其惰性。另外,lazy是一种昂贵的实例化方法。。。
static Helper singleton = new Helper();