this逃逸

title:this逃逸

date:2017年11月11日18:17:51


我们在说Java内存模型的时候提到了final域在初始化保证线程安全的相关特性。里面有句话是这样说的:

被final修饰的字段在构造器中一旦被初始化完成,并且构造器没有把“this”的引用传递出去(this引用逃逸是一件很危险的事情,其他线程有可能通过这个引用访问到“初始化了一半”的对象),那么在其他线程中就能看到final字段的值,而且其外、外部可见状态永远也不会改变。它所带来的安全性是最简单最纯粹的。

关于fina域的使用,我们在讲DCL(双重检查加锁)的时候讲过,可以将成员变量定义为final修饰的,这样就能让其他线程看到构造方法中能看到构造方中设置的值。在java5以及之后的版本,ifinal变量一旦在构造函数中设置完成(前提是在构造函数中没有泄露this引用),其它线程必定会看到在构造函数中设置的值。而DCL的问题正好在于看到对象的成员变量的默认值,因此我们可以将LazySingleton的someField变量设置成final,这样在java5中就能够正确运行了。

我们注意到,他这里强调了前提是构造函数中没有泄露this引用。那么什么是泄露this引用呢,泄露this引用即发生this逃逸:当内部类代码执行的时候,外部类对象的创建过程很有可能还没结束,这个时候如果内部类访问外部类中的数据,很有可能得到还没有正确初始化的数据。

我们来看一实例:

package com.wangcc.MyJavaSE.thread.escape;

/**
 * @ClassName: EscapeTest
 * @Description:在构造函数中使用内部类,并且代码可能会出现并行。也就是说,当内部类代码执行的时候,外部类对象的创建过程很有可能还没结束,这个时候如果内部类访问外部类中的数据,很有可能得到还没有正确初始化的数据。
 * @author wangcc
 * @date 2017年11月11日 下午6:08:36
 *       
 */
public class EscapeTest {
    private int weight = 0;

    public EscapeTest() {
        new Thread(new EscapeRunnable()).start();

        // 模拟构造函数耗时
        for (int i = 0; i < 1000000; i++) {

        }
        weight = 1;
    }

    private class EscapeRunnable implements Runnable {

        public void run() {
            System.out.println(EscapeTest.this.weight);
        }
    }

    public static void main(String[] args) {
        new EscapeTest();
    }
}

运行代码,得到的输出会是0,是外部类的默认值而不是构造方法中所赋的值。

我们可以看到:在构造函数返回之前其他线程就持有该对象的引用,调用尚未构造完全的对象的方法可能引发错误。这里跟DCL引发的问题很相似,DCL问题的出现是我们误认为new Instance是一个原子操作,而其实不是,又因为JVM的指令重排,所以会导致先返回对象应用再进行初始化工作,导致出错

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值