锁定老帖子 主题:对于单例模式的一点想法
精华帖 (1) :: 良好帖 (9) :: 新手帖 (11) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2008-07-04
虽然简单,但要获得一个高性能且线程安全的单例确不简单。 最简单的、成熟的单例实现有如下两种: 1. public static final Singleton INSTANCE=new Singleton(); 即在声明静态变量时就实例化。这种方法的问题是,不能传入构造参数从而动态的创建实例。 2. public static synchronized Singleton getInstance(){...} 即在方法上同步。这种方法的问题是,始终有同步的开销(虽然对很多应用来说这开销并不大,以致不需要考虑),而更理想的情况是,读操作不需要同步,只在创建实例时同步。 看上去更好的方法(但有问题!)是: Double-checked synchronization, 如: private static Singleton INSTANCE; public static Singleton getInstance(){ if(INSTANCE==null){ synchronized(Singelton.class){ //Double checking if(INSTANCE==null){ INSTANCE=new Singleton(); } } } } 问题解释如下: 参考1:http://www.ibm.com/developerworks/java/library/j-dcl.html 参考2:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html 在参考1中提到,out-of-order writes是原因,就是说INSTANCE=new Singleton();这行代码并不是一定按如下伪代码顺序进行的: 1.分配内存 2.调用构造器 3.赋值给INSTANCE 在有的JIT上会编译优化为: 1.分配内存 2.赋值给INSTANCE 3.调用构造器 这就是所谓的out-of-order writes。则问题会出在第2步:此时判断(INSTANCE==null)已经返回真了,但构造器还未调用完成,此时访问INSTANCE则会出现不可预料的问题。 以上都是简单的重复广为人知的知识,下面是我的补充: 在参考2中的"It will work for 32-bit primitive values"一节给了我启发,它提到对32位的原始类型的Double-checked locking是可以的,(我认为实际关键点在于 赋值操作是否是原子的)。既然对int的赋值是原子的,我们可以稍加改进,引入一个int hasInitialized: private static int hasInitialized=0; private static Singleton INSTANCE; public static Singleton getInstance(){ if(hasInitialized==0){ synchronized(Singelton.class){ //Double checking if(hasInitialized==0){ INSTANCE=new Singleton(); hasInitialized=1; } } } } 区别在于: 以hasInitialized==0来判断是否初始化完成,而在NSTANCE=new Singleton();之后才赋值以确认初始化完成。 这样不是既可保持高性能(绝大部分情况下没有锁,不进入需同步的块)、又可保证线程安全么? 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-07-04
Double-checked synchronization,将instance声明为volatile即可,在jdk5以上版本。
|
|
返回顶楼 | |
发表时间:2008-07-04
private static int hasInitialized=0;
private static Singleton INSTANCE; public static synchronized Singleton getInstance(){ if(hasInitialized==0){ synchronized(Singelton.class){ //Double checking if(hasInitialized==0){ INSTANCE=new Singleton(); hasInitialized=1; } } } } |
|
返回顶楼 | |
发表时间:2008-07-04
Lucas Lee 写道 private static int hasInitialized=0; private static Singleton INSTANCE; public static synchronized Singleton getInstance(){ if(hasInitialized==0){ synchronized(Singelton.class){ //Double checking if(hasInitialized==0){ INSTANCE=new Singleton(); hasInitialized=1; } } } } 区别在于: 以hasInitialized==0来判断是否初始化完成,而在NSTANCE=new Singleton();之后才赋值以确认初始化完成。 这样不是既可保持高性能(绝大部分情况下没有锁,不进入需同步的块)、又可保证线程安全么? JVM 不保证代码INSTANCE=new Singleton() 一定在hasInitialized=1之前执行!!! |
|
返回顶楼 | |
发表时间:2008-07-04
除了你已经提出的两种办法,没有其他可以run everywhere的办法了。
|
|
返回顶楼 | |
发表时间:2008-07-04
weiqingfei 写道 private static int hasInitialized=0;
private static Singleton INSTANCE; public static synchronized Singleton getInstance(){ if(hasInitialized==0){ synchronized(Singelton.class){ //Double checking if(hasInitialized==0){ INSTANCE=new Singleton(); hasInitialized=1; } } } } 谢谢提醒,我已经修改了,笔误。 |
|
返回顶楼 | |
发表时间:2008-07-04
slangmgh 写道 JVM 不保证代码INSTANCE=new Singleton() 一定在hasInitialized=1之前执行!!! 这个不会吧?你的证据呢? 我提到的out-of-order write只是对INSTANCE=new Singleton()范围而言。 连两条语句的顺序都无法保证,这个就难以想象了。。。 |
|
返回顶楼 | |
发表时间:2008-07-04
biubiu 写道 除了你已经提出的两种办法,没有其他可以run everywhere的办法了。
我最后提出的一种方案不行么? |
|
返回顶楼 | |
发表时间:2008-07-04
# private static Singleton INSTANCE;
# public static Singleton getInstance(){ # if(INSTANCE==null){ # synchronized(Singelton.class){ # //Double checking # if(INSTANCE==null){ # INSTANCE=new Singleton(); # } # } # } # } 1. private static int hasInitialized=0; 2. private static Singleton INSTANCE; 3. public static Singleton getInstance(){ 4. if(hasInitialized==0){ 5. synchronized(Singelton.class){ 6. //Double checking 7. if(hasInitialized==0){ 8. INSTANCE=new Singleton(); 9. hasInitialized=1; 10. } 11. } 12. } 13. } 差不多吗 |
|
返回顶楼 | |
发表时间:2008-07-05
yxbbing 写道 # private static Singleton INSTANCE;
# public static Singleton getInstance(){ # if(INSTANCE==null){ # synchronized(Singelton.class){ # //Double checking # if(INSTANCE==null){ # INSTANCE=new Singleton(); # } # } # } # } 1. private static int hasInitialized=0; 2. private static Singleton INSTANCE; 3. public static Singleton getInstance(){ 4. if(hasInitialized==0){ 5. synchronized(Singelton.class){ 6. //Double checking 7. if(hasInitialized==0){ 8. INSTANCE=new Singleton(); 9. hasInitialized=1; 10. } 11. } 12. } 13. } 差不多吗 你没有仔细研究第一个方法为什么有问题,及第二种方法为什么解决了它的问题,当然会觉得差不多了。 |
|
返回顶楼 | |