最近在看 headfirst 在 singleton这里我有些问题。。想请教 javaeye 的朋友
贴代码:
public class MyClass {private static MyClass uniqueInstance; private MyClass(){} public static synchronized MyClass getInstance(){ if(uniqueInstance == null){ return new MyClass(); } return uniqueInstance; }
public class Singleton {
private static Singleton uniqueInstance = new Singleton();private Singleton() { } public static Singleton getInstance() { return uniqueInstance; }
}
public class SingletonTest {private volatile static SingletonTest uniqueInstance; private SingletonTest(){} public static SingletonTest getInstance(){ if(uniqueInstance == null){ synchronized(SingletonTest.class){ if(uniqueInstance==null){ uniqueInstance = new SingletonTest(); } } } return uniqueInstance; }
}
性能主要解决之处想到 减少对象创建 减少处理时间(减少同步)
使用同步 处理就是代码中多了很多判断 一样
减少对象 第二种很明确 一般对象只是创建的话 多使用静态 让所有实例来共享 这样性能高 系统不用每次创建了---减少了创建
同步必须要做的时候 当然是在必须的地方做. 第三种就写在了 对象未null的情况
这样 不进if就不会有同步 ---同步降到最低
一般情况 有线程考虑到地方 使用多个锁 不要使用一个锁---理论上会加大并发数
个人愚见,不对请拍砖:
第一种,因为同步,串行处理,在高并发的时候,影响效率。
第二种,解决了效率也解决了线程安全问题。
第三种,在多线程高并发的时候第一次访问也存在效率问题,比如第一次同时有100人访问, 第一个if(uniqueInstance == null),最坏情况会有100人if里面,synchronized(SingletonTest.class)同步产生串行处理。第101人访问的时候才能体现出效果。
我认为还是第二种比较好,第三种吗也挺好,还是看使用场景,如果是类似秒杀的情况用第二种,如果不是秒杀,系统又需要延迟加载(或者使用的时候在加载)用第三种。
[quote="whao189"][quote="chenyongxin"]个人愚见,不对请拍砖:
第一种,因为同步,串行处理,在高并发的时候,影响效率。
第二种,解决了效率也解决了线程安全问题。
第三种,在多线程高并发的时候第一次访问也存在效率问题,比如第一次同时有100人访问, 第一个if(uniqueInstance == null),最坏情况会有100人if里面,synchronized(SingletonTest.class)同步产生串行处理。第101人访问的时候才能体现出效果。
我认为还是第二种比较好,第三种吗也挺好,还是看使用场景,如果是类似秒杀的情况用第二种,如果不是秒杀,系统又需要延迟加载(或者使用的时候在加载)用第三种。[/quote]
但是。。。针对第二种情况 我有些不明白什么 叫 ”在创建和运行时方面的 负担繁重“。。。。。
说真的。。我对这个概念很模糊。。。能解释一下么???[/quote]
第二种就是当你new MyClass 的时候 MyClass对象就创建了,如果你的系统中有无数个这样的对象,系统启动的时候就开始创建对象,如果对象里面还有一些排他性资源加载那就慢了。
第二种情况,这个类一定是提前创建完成了,假设这个对象你90%的情况下用不到,但是一创建就要占用你70%的系统资源,这就是“在创建和运行时方面的 负担繁重”,肯定是希望这个对象在需要的时候在创建,而不是一直在那里占用资源。
第一种情况,每次在使用这个对象前都要锁住判断对象的状态,假设100个线程并发就要99个人等待,即使这个对象已经存在了。
第三种情况,很多时候是比较推荐的,算是在性能和安全之间进行了平衡。目的就是如果对象已经存在的时候,就不需要并发锁进行控制了,直接拿去用就行。如果对象不存在,再通过并发所控制只生成一个对象。所以要双重判断
DCL争议这么大,怎么还用呢,难道以前是做C++的么
虽然见过有人说jdk 1.5后 加volatile 就能支持dcl,但只是说,却无reference证明...
替代方法用holder,直接让类加载锁(不知道这个名词是否合理) 帮你保证 instance安全
singleton pattern的最佳实践还是用 枚举 (不是我说的...Joshua Blcoh说的...)
holder + 序列化
[code="java"]public final class Foo implements Serializable {
private static final long serialVersionUID = 1L;
private static class FooLoader {
private static final Foo INSTANCE = new Foo();
}
private Foo() {
if (FooLoader.INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
return FooLoader.INSTANCE;
}
// readResolve method to preserve singleton property
@SuppressWarnings("unused")
private Foo readResolve() {
return FooLoader.INSTANCE;
}
}
[/code]
直接支持序列化
[code="java"]enum Singleton {
INSTANCE;
public static Singleton getInstance() {
return INSTANCE;
}
} [/code]
面试的时候一听到 单例就能吹出一大片东西来了。
优先使用第2种,第3种同样存在并发危险
个人观点:
你拿第二种和第一种对比一下,第一个就在创建和运行时方面的,负担繁重,因为不管是创建或者获得,都是同步的,如果100个程序都在调用这个方法,那么由于是同步的,只能一个个的执行。
而第二个,由于是静态的instance,在程序刚启动,就已经初始化好了,所以在运行时不需要判断是否该instance已经存在,也不需要去同步。
lz,这个问题我都已经说的这么清楚了,该给分了吧 呵呵