精华帖 (0) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-09-02
最后修改:2010-09-09
这几天在看《深入浅出设计模式》,看到了单例模式这一章。
---------------------------------------------------(9.9) 单例的范围: 今天看到了chjavach兄写的研磨设计模式系列. 关于他对"单例模式"的研磨中的"单例模式的范围"的叙述,我有不同的看法.
在他的研磨中是这么描述的: " 也就是在多大范围内是单例呢?
他提到的是"虚拟机的范围",并说明了"一个虚拟机在通过自己的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. 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-09-02
引用 2.急切(eagerly)实例化,也就是声明实例的引用变量时直接生成一个实例。 private static Singleton st=new Singleton(); 优点:万金油,没有突出的缺点。 缺点:被类加载器加载时就可能(与JVM的实现也有关系)生成实例,而非用到时生成。--并非不可接受。 这个最好能声明成final,因为按照新的(java 5以后)的JMM(Java Memory Model)中对于final的语义定义,可以确保你收到的实例是完整的。没有定义成final似乎不能有这个保证。 |
|
返回顶楼 | |
发表时间:2010-09-02
引用 缺点:java1.4之前的许多jvm,使用volatile会导致双重检测加锁失效。(HeadFirst原话)。 Java 5之前,volatile语义只定义了指定的volatile变量的内容visible, 但没有定义volatile相关的其他变量的visibility,更没有定义与其相关的 语句在执行优化时有可能会导致的reorder问题。 Java 5之后引入了新的JMM语义定义,这些问题基本得到解决。但 双重锁还不是被推荐使用,具体是什么原因,我没有认真看,不懂。 |
|
返回顶楼 | |
发表时间:2010-09-02
引用 这里有个更NB的解决方案。懒汉通用无锁。 http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom 利用内部类来实现单例念头之前也闪了一下,还没细想,就看到了这篇文章。 这是依据Java内部类装入机制设计的一种实现方法。 Java内部类只有在第一次使用时才装入。 |
|
返回顶楼 | |
发表时间:2010-09-03
jeff.key 写道 引用 2.急切(eagerly)实例化,也就是声明实例的引用变量时直接生成一个实例。
private static Singleton st=new Singleton(); 优点:万金油,没有突出的缺点。 缺点:被类加载器加载时就可能(与JVM的实现也有关系)生成实例,而非用到时生成。--并非不可接受。 这个最好能声明成final,因为按照新的(java 5以后)的JMM(Java Memory Model)中对于final的语义定义,可以确保你收到的实例是完整的。没有定义成final似乎不能有这个保证。 你确认么?static + final 修饰?常量?不能被修改属性的实例...那这个单例还有什么价值没有? |
|
返回顶楼 | |
发表时间:2010-09-03
static + final 修饰,不能修改实例的属性?你听谁说的
|
|
返回顶楼 | |
发表时间:2010-09-03
最后修改:2010-09-03
jeff.key 写道 引用 2.急切(eagerly)实例化,也就是声明实例的引用变量时直接生成一个实例。
private static Singleton st=new Singleton(); 优点:万金油,没有突出的缺点。 缺点:被类加载器加载时就可能(与JVM的实现也有关系)生成实例,而非用到时生成。--并非不可接受。 这个最好能声明成final,因为按照新的(java 5以后)的JMM(Java Memory Model)中对于final的语义定义,可以确保你收到的实例是完整的。没有定义成final似乎不能有这个保证。 请问"实例是完整的"这句话是什么意思呢? 加了final固然最好 但在HeadFirst中并没有提到要加(实际上它确实也没加) 有没有证据证明不加会出现问题?这种说法的出处? --谢谢icanfly在第二页给出的说法,刚好也解释了这里说的完整性的问题...不过我觉得急切加载应该不会碰上,JVM在加载这个类的时候就实例化了这个对象,不会因为多线程的问题导致这个对象不完整(多线程尚未启动) 楼上的qjtttt兄显然还没明白static+final的含义... 另外,关于双检测中volatile的必要性产生了疑问 在core java(第八版)的14.5.8章节以及think in java(第四版)的21.3.3章节 都有这么一种描述: 同步机制强制在处理器系统中,一个任务作出的修改必须在另一个任务中是可视的...如果一个域完全由synchronized方法或语句块来防护,那就不必将其设置为是volatile的.(出自think in java) 如果你用锁来保护可以被多个线程访问的代码,那么可以不用考虑这种问题(出自core java,"问题"是指可视性) 如果按照这两本书的说法,双检测中已经使用了同步锁防护,那么那个属性就没必要使用volatile了.请大家指正下. 另外,"构造函数中泄漏 this"到底是什么意思啊... - -! |
|
返回顶楼 | |
发表时间:2010-09-03
runshine 写道 private volatile static BgImage singleton = null; if(singleton==null) { synchronized(Singleton .class) { if(singleton==null) { singleton = new Singleton (); } } } private static Singleton instance= null; private final static Object classLock=new Object(); public static Singleton getInstance(){ synchronized(classLock) { if(singleton==null) { instance= new Singleton (); } return instance; } } 没有具体研究过,对于类锁的影响会有多大。反正我的程序里是不会有影响的。 我一般这样写,不被调用也只是创建了一个object,应该可以接受吧。 synchronize一般我会放在最外层。 |
|
返回顶楼 | |
发表时间:2010-09-03
qjtttt 写道 jeff.key 写道 引用 2.急切(eagerly)实例化,也就是声明实例的引用变量时直接生成一个实例。
private static Singleton st=new Singleton(); 优点:万金油,没有突出的缺点。 缺点:被类加载器加载时就可能(与JVM的实现也有关系)生成实例,而非用到时生成。--并非不可接受。 这个最好能声明成final,因为按照新的(java 5以后)的JMM(Java Memory Model)中对于final的语义定义,可以确保你收到的实例是完整的。没有定义成final似乎不能有这个保证。 你确认么?static + final 修饰?常量?不能被修改属性的实例...那这个单例还有什么价值没有? 这位兄弟对final的作用有点误解,static + final 修饰只是说该引用不能指向别的对象了,并非说不能修改该实例的属性。 |
|
返回顶楼 | |
发表时间:2010-09-03
最后修改:2010-09-03
fool_leave 写道 private static Singleton instance= null; private final static Object classLock=new Object(); public static Singleton getInstance(){ synchronized(classLock) { if(singleton==null) { instance= new Singleton (); } return instance; } } 没有具体研究过,对于类锁的影响会有多大。反正我的程序里是不会有影响的。 我一般这样写,不被调用也只是创建了一个object,应该可以接受吧。 synchronize一般我会放在最外层。 你这种写法与方案1:直接static synchronized getInstance().是一样的 每一次取实例都会锁整个类.安全性与通用性上没什么问题.但在多线程中性能损失会很大. |
|
返回顶楼 | |