论坛首页 Java企业应用论坛

关于单例及其范围...以及构造函数中泄漏 this

浏览 9221 次
精华帖 (0) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-09-03  
引用
另外请教大家个问题
在构造方法中使用 xxx=this,尽管没有错误,但是NetBeans会提示"构造函数中泄漏 this"...在一般方法中使用没有提示...这是什么意思?会有什么问题?

可以看看java并发编程这书,讲得非常不错,我引用它的一段原话
引用

对象只有通过构造函数返回后,才处于可预言、稳定的状态,所以从构造函数内部发布的对象,只是一个未完成构造的对象。甚至即使是在构造函数的最后一行发布的引用也是如此。如果this引用在构造过程中逸出,这个的对象被认为是“没有正确构建的”

引用
不要让this引用在构造期间逸出

0 请登录后投票
   发表时间:2010-09-03  
icanfly 写道
引用
另外请教大家个问题
在构造方法中使用 xxx=this,尽管没有错误,但是NetBeans会提示"构造函数中泄漏 this"...在一般方法中使用没有提示...这是什么意思?会有什么问题?

可以看看java并发编程这书,讲得非常不错,我引用它的一段原话
引用

对象只有通过构造函数返回后,才处于可预言、稳定的状态,所以从构造函数内部发布的对象,只是一个未完成构造的对象。甚至即使是在构造函数的最后一行发布的引用也是如此。如果this引用在构造过程中逸出,这个的对象被认为是“没有正确构建的”

引用
不要让this引用在构造期间逸出



谢谢谢谢
这也刚好对实例完整性的的意思进行了阐述

但jeff.key提到的完整性问题,我觉得急切加载这个问题上应该不会碰上:JVM在加载这个类的时候就实例化了这个对象,不会因为多线程的问题导致这个对象不完整(多线程尚未启动)
0 请登录后投票
   发表时间:2010-09-03  
panhf2003 写道
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 修饰只是说该引用不能指向别的对象了,并非说不能修改该实例的属性。

哦哦,平时不太会定义这样的常量,大多都是基本类型,而且一般都不去修改,搞混了,呵呵
0 请登录后投票
   发表时间:2010-09-04  
runshine 写道
请问"实例是完整的"这句话是什么意思呢?


其实双重检查是可行的,不过要保证实例的完整构造后才能使用
//3.双重检测加锁。 
private static BgImage instance= null; 

if(instance==null) { 
            synchronized(Singleton.class) { 
                if(instance==null) {
                    BgImage tmp = new Singleton();
//singleton = new Singleton ();new 只是把地址给了singleton,但是此时还没有执行构造器,也就是说singleton只分配了内存,但是内部的成员变量等还没有初始化,即singleton此时代表一个不完整的实例,其他线程可能拿到此实例(因为此时singleton!=null,但singleton不完整)
                    instance = tmp;//此时tmp已经完整了
                } 
            } 
        } 


runshine 写道
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"到底是什么意思啊... - -!

0 请登录后投票
   发表时间:2010-09-04   最后修改:2010-09-04
runshine 写道
请问"实例是完整的"这句话是什么意思呢?


其实双重检查是可行的,不过要保证实例的完整构造后才能使用
//3.双重检测加锁。 
private static BgImage instance= null; 

if(instance==null) { 
            synchronized(Singleton.class) { 
                if(instance==null) {
                    BgImage tmp = new Singleton();
//singleton = new Singleton ();new 只是把地址给了singleton,但是此时还没有执行构造器,也就是说singleton只分配了内存,但是内部的成员变量等还没有初始化,即singleton此时代表一个不完整的实例,其他线程可能拿到此实例(因为此时singleton!=null,但singleton不完整)
                    instance = tmp;//此时tmp已经完整了
                } 
            } 
        } 


   看着楼上发的代码,真痛心,代码写的好乱(拼写错误好多啊),尽管表达了自己的意思.但是你这样写,与直接new出一个对象赋值给实例的私有静态属性有何不同?
0 请登录后投票
   发表时间:2010-09-05  
static + final 修饰只是说该引用不能指向别的对象了,并非说不能修改该实例的属性。

应该是这样的 !!lz误解了吧、

另外你说的那个遗漏问题!做等高手吧!@!
0 请登录后投票
   发表时间:2010-09-05  
其实哪些所谓双重检查更能导致的问题,jdk应该解决了吧
这么多年了,我反正平时都是用得双重检查也没有发现问题
0 请登录后投票
   发表时间:2010-09-05  
^ ^ 我的代码只是复制了lz的,然后修改了下
但是你这样写,与直接new出一个对象赋值给实例的私有静态属性有何不同?
看书时看到过,要分析ClassName instance = new ClassName();后产生的汇编代码,才知道为什么会这样
罪魁祸首就是运行时优化,导致指令执行顺序发生调换A test case showing that it doesn't work一节有汇编码


首先明确需要单例构造的实例,都是重对象——比如HibernateSessionFactory,他们需要的时间比较长,可能需要500ms
大意是:new操作后堆分配了内存这样instance就非空了,但是此时对象构造器还没执行——我们称之为不完整对象(他的构造器还没有运行呢,大量的成员变量还没有构造完,需要大约500ms才可完全初始化完毕);
但是:既然instance非空,那么其他线程执行getInstance()时因为检测到instance非空,就得到了此对象,从而执行instance.aMethod();鬼知道会发生什么事情



看着楼上发的代码,真痛心,代码写的好乱(拼写错误好多啊),尽管表达了自己的意思.但是你这样写,与直接new出一个对象赋值给实例的私有静态属性有何不同?


fengjia10 写道
skzr.org 写道
runshine 写道
请问"实例是完整的"这句话是什么意思呢?


其实双重检查是可行的,不过要保证实例的完整构造后才能使用
//3.双重检测加锁。 
private static BgImage instance= null; 

if(instance==null) { 
            synchronized(Singleton.class) { 
                if(instance==null) {
                    BgImage tmp = new Singleton();
//singleton = new Singleton ();new 只是把地址给了singleton,但是此时还没有执行构造器,也就是说singleton只分配了内存,但是内部的成员变量等还没有初始化,即singleton此时代表一个不完整的实例,其他线程可能拿到此实例(因为此时singleton!=null,但singleton不完整)
                    instance = tmp;//此时tmp已经完整了
                } 
            } 
        } 


   看着楼上发的代码,真痛心,代码写的好乱(拼写错误好多啊),尽管表达了自己的意思.但是你这样写,与直接new出一个对象赋值给实例的私有静态属性有何不同?

0 请登录后投票
   发表时间:2010-09-05  
^ ^ 我的代码只是复制了lz的,然后修改了下
但是你这样写,与直接new出一个对象赋值给实例的私有静态属性有何不同?
看书时看到过,要分析ClassName instance = new ClassName();后产生的汇编代码,才知道为什么会这样
罪魁祸首就是运行时优化,导致指令执行顺序发生调换A test case showing that it doesn't work一节有汇编码


首先明确需要单例构造的实例,都是重对象——比如HibernateSessionFactory,他们需要的时间比较长,可能需要500ms
大意是:new操作后堆分配了内存这样instance就非空了,但是此时对象构造器还没执行——我们称之为不完整对象(他的构造器还没有运行呢,大量的成员变量还没有构造完,需要大约500ms才可完全初始化完毕);
但是:既然instance非空,那么其他线程执行getInstance()时因为检测到instance非空,就得到了此对象,从而执行instance.aMethod();鬼知道会发生什么事情



fengjia10 写道
看着楼上发的代码,真痛心,代码写的好乱(拼写错误好多啊),尽管表达了自己的意思.但是你这样写,与直接new出一个对象赋值给实例的私有静态属性有何不同?


fengjia10 写道
skzr.org 写道
runshine 写道
请问"实例是完整的"这句话是什么意思呢?


其实双重检查是可行的,不过要保证实例的完整构造后才能使用
//3.双重检测加锁。 
private static BgImage instance= null; 

if(instance==null) { 
            synchronized(Singleton.class) { 
                if(instance==null) {
                    BgImage tmp = new Singleton();
//singleton = new Singleton ();new 只是把地址给了singleton,但是此时还没有执行构造器,也就是说singleton只分配了内存,但是内部的成员变量等还没有初始化,即singleton此时代表一个不完整的实例,其他线程可能拿到此实例(因为此时singleton!=null,但singleton不完整)
                    instance = tmp;//此时tmp已经完整了
                } 
            } 
        } 


   看着楼上发的代码,真痛心,代码写的好乱(拼写错误好多啊),尽管表达了自己的意思.但是你这样写,与直接new出一个对象赋值给实例的私有静态属性有何不同?


既然知道优化会重排序,那么你的
引用
instance = tmp;//此时tmp已经完整了
这句话你能保证不会被排序到 BgImage tmp = new Singleton();这个的中间环节或是什么的吗?你这种做法好像国外早就有论证是错误的吧,具体哪篇帖子给忘了。
0 请登录后投票
   发表时间:2010-09-05  
skzr.org 写道
^ ^ 我的代码只是复制了lz的,然后修改了下
但是你这样写,与直接new出一个对象赋值给实例的私有静态属性有何不同?
看书时看到过,要分析ClassName instance = new ClassName();后产生的汇编代码,才知道为什么会这样
罪魁祸首就是运行时优化,导致指令执行顺序发生调换A test case showing that it doesn't work一节有汇编码


首先明确需要单例构造的实例,都是重对象——比如HibernateSessionFactory,他们需要的时间比较长,可能需要500ms
大意是:new操作后堆分配了内存这样instance就非空了,但是此时对象构造器还没执行——我们称之为不完整对象(他的构造器还没有运行呢,大量的成员变量还没有构造完,需要大约500ms才可完全初始化完毕);
但是:既然instance非空,那么其他线程执行getInstance()时因为检测到instance非空,就得到了此对象,从而执行instance.aMethod();鬼知道会发生什么事情



fengjia10 写道
看着楼上发的代码,真痛心,代码写的好乱(拼写错误好多啊),尽管表达了自己的意思.但是你这样写,与直接new出一个对象赋值给实例的私有静态属性有何不同?


fengjia10 写道
skzr.org 写道
runshine 写道
请问"实例是完整的"这句话是什么意思呢?


其实双重检查是可行的,不过要保证实例的完整构造后才能使用
//3.双重检测加锁。 
private static BgImage instance= null; 

if(instance==null) { 
            synchronized(Singleton.class) { 
                if(instance==null) {
                    BgImage tmp = new Singleton();
//singleton = new Singleton ();new 只是把地址给了singleton,但是此时还没有执行构造器,也就是说singleton只分配了内存,但是内部的成员变量等还没有初始化,即singleton此时代表一个不完整的实例,其他线程可能拿到此实例(因为此时singleton!=null,但singleton不完整)
                    instance = tmp;//此时tmp已经完整了
                } 
            } 
        } 


   看着楼上发的代码,真痛心,代码写的好乱(拼写错误好多啊),尽管表达了自己的意思.但是你这样写,与直接new出一个对象赋值给实例的私有静态属性有何不同?


既然知道优化会重排序,那么你的
引用
instance = tmp;//此时tmp已经完整了
这句话你能保证不会被排序到 BgImage tmp = new Singleton();这个的中间环节或是什么的吗?你这种做法好像国外早就有论证是错误的吧,具体哪篇帖子给忘了。

好像就是这个 http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics