这几天在看《深入浅出设计模式》,看到了单例模式这一章。
关于单例想必大家都很熟悉了。
而为了线程安全,HeadFirst中列出了三种方案,并对优缺点做了说明。
1.直接static synchronized getInstance().
优点:简单。
缺点:类锁,且每次调用都会把类锁住,效率不高。--实在令人难以接受
2.急切(eagerly)实例化,也就是声明实例的引用变量时直接生成一个实例。
private static Singleton st=new Singleton();
优点:万金油,没有突出的缺点。
缺点:被类加载器加载时就可能(与JVM的实现也有关系)生成实例,而非用到时生成。--并非不可接受。
3.双重检测加锁。(关于DCL,这里有片极为精彩的论述:http://www.iteye.com/topic/260515,所有问题都可以退散了)
private volatile static BgImage singleton = null;
if(singleton==null)
{
synchronized(Singleton .class)
{
if(singleton==null)
{
singleton = new Singleton ();
}
}
}
优点:延迟实例化,除了第一次得到对象实例会锁住类其余情况不会锁。
缺点:java1.4之前的许多jvm,使用volatile会导致双重检测加锁失效。(HeadFirst原话)。而不使用volatile可能会因更新不及时产生多个实例的可能。第一次生成实例依然会锁类。--若非java1.4及更早版本用户,此方案可接受度最高。
很明显三种方案中没有哪种方案是完美的。
个人贪心不足希望集以上各种优点于一身,通用、延迟初始化,因此想了一种方案。
囧...发现想法有缺陷,删之.
-----------------
关于方案3双重检测失效的问题,这里有很详细的描述,不过作者的JDK比1.3还要老... - -! 还是埋了吧.
http://ajava.org/course/java/13502.html
这里有个更NB的解决方案。懒汉通用无锁。
http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom
利用内部类来实现单例念头之前也闪了一下,还没细想,就看到了这篇文章。
JE之前也有过讨论
http://www.iteye.com/topic/691223?page=3
关于DCL,这里有片极为精彩的论述:http://www.iteye.com/topic/260515,所有问题都可以退散了
唉,想法失败了,内容撞墙了
版主您随意.
另外请教大家个问题
在构造方法中使用 xxx=this,尽管没有错误,但是NetBeans会提示"构造函数中泄漏 this"...在一般方法中使用没有提示...这是什么意思?会有什么问题?
---------------------------------------------------(9.9)
单例的范围:
今天看到了chjavach兄写的研磨设计模式系列.
关于他对"单例模式"的研磨中的"单例模式的范围"的叙述,我有不同的看法.
在他的研磨中是这么描述的:
" 也就是在多大范围内是单例呢?
观察上面的实现可以知道,目前Java里面实现的单例是一个虚拟机的范围。因为装载类的功能是虚拟机的,所以一个虚拟机在通过自己的ClassLoader装载饿汉式实现的单例类的时候就会创建一个类的实例。
这就意味着如果一个机器上有多个虚拟机,那么每个虚拟机里面都应该有一个这个类的实例,但是整个机器上就有很多个实例了。
另外请注意一点,这里讨论的单例模式并不适用于集群环境,对于集群环境下的单例这里不去讨论,那不属于这里的内容范围。"
他提到的是"虚拟机的范围",并说明了"一个虚拟机在通过自己的ClassLoader装载"
多个的虚拟机拥有多个实例这固然是正确的.但在一个虚拟机中就能保证只存在一个单例类的实例吗?
答案是否定的.
chjavach兄提到了ClassLoader,却忽视了一个虚拟机能够有多个ClassLoader.一个虚拟机中不同的ClassLoader装载同一个单例类,是会得到多个实例的.
这种情况在一般的程序中可能很少见,不过在java web应用中可能就比较常见了.
因为每个web application都有一个属于自己的ClassLoader.
以tomcat6为例:其根目录下的lib文件夹里的jar是由一个ClassLoader(StandardClassLoader)装载的,而webapps文件夹下的应用是由WebappClassLoader装载的(且每个应用的WebappClassLoader的context不同)。StandardClassLoader是WebappClassLoader的Parent ClassLoader.
我们打包一个Singleton类放到tomcat/lib文件夹下,然后写一个拥有Singletonx的应用,这个应用下的index.jsp同时引用两个单例类
把这个应用放到webapps里,命名为webtest
第一次运行index.jsp会看到Singleton与Singletonx都是第一次装载实例化,此后每次运行index.jsp都会分别得到Singleton与Singletonx的同一个实例.
然后进入tomcat的管理界面,把webtest应用reload一次后再次运行index.jsp
这时会看到Singleton的实例仍然是reload之前的那个,而Singletonx会重新被加载并实例化一次.
因为reload会销毁webtest的ClassLoader(WebappClassLoader),但不会影响StandardClassLoader.
从而得出单例模式的范围是一个ClassLoader及其子ClassLoader而非整个JVM.
|