这种singleton实现为什么就是线程安全的呢

deprecatedgb 2011-02-22 01:33:07
摘自:http://en.wikipedia.org/wiki/Singleton_pattern


public class Singleton {

// Private constructor prevents instantiation from other classes
private Singleton() {
System.out.println("constructor");
}

/**
* SingletonHolder is loaded on the first execution of
* Singleton.getInstance() or the first access to SingletonHolder.INSTANCE,
* not before.
*/
private static class SingletonHolder {
public static final Singleton INSTANCE = new Singleton();
}

public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}

public static void main(String[] args) {
System.out.println("main");
getInstance();
}

}

上面说:
The nested class is referenced no earlier (and therefore loaded no earlier by the class loader) than the moment that getInstance() is called. Thus, this solution is thread-safe without requiring special language constructs (i.e. volatile or synchronized).

内嵌类在getInstance被调用的时候才被引用到,就保证了它是线程安全的,我想凭什么呢,难道就是因为这个原因吗,就不是因为INSTANCE是final的或其它什么原因? 如果说是因为getInstance的时候才被引用到所以才安全,那意思就是随类一起加载的eager type singleton就不是线程安全的了?据我所知那更是线程安全的啊。而且,这里这个thus是表因果关系,那个“因”感觉有点不靠谱,不知是不是我哪里理解有误~

假设这样一种情景,如果两个线程同时调用getInstance,且同时去初始化下面这句:
public static final Singleton INSTANCE = new Singleton();

那这时候如果它还是线程安全的,又是什么保证的呢

...全文
755 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
deprecatedgb 2012-08-08
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 的回复:]

Use this pattern if the initialization of the class is expensive and it cannot be done safely at class-loading time
按理如4楼所说,java spec保证了虚拟机在类加载阶段即class-loading time对于eager type singleton是线程安全的,因为类只加……
[/Quote]
现在已比较确认是jvm保证静态变量只初始化一次,因此被冠以“安全”之名。
王二北 2011-02-24
  • 打赏
  • 举报
回复
Java中类块初始化时是默认线程安全的,只有在调用getInstance()时,才能初始化内部类中的public static final Singleton INSTANCE = new Singleton();
1.如果使用线程安全的LAZY模式,就会降低性能,但内存使用率提上去了(只有在使用时才创建对象,虽然只有一次)
2.如果使用“饿汉”模式,直接声明一个类成员变量,不用考虑线程安全的问题,性能提高了,但内存使用率下去了。
3.“双重检查加锁模式”
如下(随便写了个例子的getInstance方法):
class A{
private static A a;
private A(){}
// 需两次判断a是否存在
public static A getInstance()
{
// 第一次
if(a==null)
{
synachronized(A.class)
{
// 第二次
if(a==null)
{
a = new A();
}
}
}
return a;
}
}
4. 枚举,这个效果和饿汉的那个差不多
enum A
{
// 唯一的实例
oneinstance;
/*
操作.......
*/
}
5.就是静态内部类,楼主已经说过了,不再赘述。
dracularking 2011-02-24
  • 打赏
  • 举报
回复
Use this pattern if the initialization of the class is expensive and it cannot be done safely at class-loading time
按理如4楼所说,java spec保证了虚拟机在类加载阶段即class-loading time对于eager type singleton是线程安全的,因为类只加载一次,单例instance也就只会被实例化一次,这里说的不安全会是什么呢,被加载多次吗?这就前后矛盾了,或者实例化会不彻底?要有资料证明才行。
Joop_Song 2011-02-24
  • 打赏
  • 举报
回复
Singleton原来也很深奥,受教了。。
deprecatedgb 2011-02-24
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 penghaiwudi 的回复:]

人家用的静态方法里面又包含了final 无法再修改 了 算是 安全了
[/Quote]
谢谢回复,不过这算是个引申问题了,能够扩展讨论一个问题就更好了
其实文中已经提到为何是线程安全的原因了,就是因为是顺序执行的,倒没有提到final
我个人也不认为final会和线程安全有直接关联,很多这种不安全的情形都是因为一个步骤是“非原子的”造成的
deprecatedgb 2011-02-24
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 silenceburn 的回复:]

java的class-loading应该是线程安全的,
如你所说道eager type singleton 就是利用这一点做的,

至于顶楼这样写,我个人觉得应该是一个利用class-loading安全实现lazy type singleton的例子。
如果不通过使用内部类的loading安全实现lazy init,那就需要双重锁或者syncronized方法了
[/Quote]
问题他恰恰是说class-loading时候不安全才用这种模式的
Use this pattern if the initialization of the class is expensive and it cannot be done safely at class-loading time
这里的说法和上文也是有相互印证的
penghaiwudi 2011-02-23
  • 打赏
  • 举报
回复
人家用的静态方法里面又包含了final 无法再修改 了 算是 安全了
silenceburn 2011-02-22
  • 打赏
  • 举报
回复
java的class-loading应该是线程安全的,
如你所说道eager type singleton 就是利用这一点做的,

至于顶楼这样写,我个人觉得应该是一个利用class-loading安全实现lazy type singleton的例子。
如果不通过使用内部类的loading安全实现lazy init,那就需要双重锁或者syncronized方法了
deprecatedgb 2011-02-22
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 qybao 的回复:]

这段英文的意思是,当getInstance调用时才会加载SingletonHolder类,
而加载SingletonHolder类时会执行public static final Singleton INSTANCE = new Singleton();生成Singleton实例
这个SingletonHolder加载执行是系统控制的,多个线程同时调用getInstance,肯定有某一时刻某个……
[/Quote]
感谢,关于double-checked locking的疑惑我可能已经解了,即使某个阶段INSTANCE不为null且对象未构造完成,但在这种模式下不影响INSTANCE被完整返回,剩下来就是前面提到的对应的类加载阶段不安全的问题,可能还需要多了解一下
deprecatedgb 2011-02-22
  • 打赏
  • 举报
回复
我后来又看了这里,算了解了一点:

http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom

When to use it

Use this pattern if the initialization of the class is expensive and it cannot be done safely at class-loading time and the initialization is highly concurrent. The crux of the pattern is the safe removal of the synchronization overhead associated with accessing a singleton instance.

其中有一种使用该模式的情况就是在class-loading阶段如果初始化不安全

Since the class initialization phase is guaranteed by the JLS to be serial, i.e., non-concurrent, no further synchronization is required in the static getInstance method during loading and initialization.
类初始化阶段是连续的,也就是非并发的

但尽管以上两条可以自圆其说,但和我以前的认识都有些冲突:
其1,class-loading阶段一般是认为线程安全的吧 因为我知道eager type singleton就是这么实现的,哪种情形会不安全呢
其2,java的构造函数是线性的,也就是安全的,但double-checked locking里提到的其产生的原因就是不因为这个构造顺序可能是存在问题的才引起的
qybao 2011-02-22
  • 打赏
  • 举报
回复
这段英文的意思是,当getInstance调用时才会加载SingletonHolder类,
而加载SingletonHolder类时会执行public static final Singleton INSTANCE = new Singleton();生成Singleton实例
这个SingletonHolder加载执行是系统控制的,多个线程同时调用getInstance,肯定有某一时刻某个线程先进入getInstance,这时就会自动触发系统加载执行,当其他线程再进入getInstance时,系统加载执行早已结束,所以是线程安全的
其实SingletonHolder没必要


50,530

社区成员

发帖
与我相关
我的任务
社区描述
Java相关技术讨论
javaspring bootspring cloud 技术论坛(原bbs)
社区管理员
  • Java相关社区
  • 小虚竹
  • 谙忆
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧