this引用逃逸

什么是this引用逃逸

指的是对象还没有构造完成,他的this引用就已经发布出去了。

先确定两个名词定义,对象的发布逸出

发布:发布一个对象的意思是指,使对象能够在当前作用域之外的代码中被使用。如,将一个指向该对象的引用保存到其他代码可以访问的地方,或者在某一个非私有的方法中返回该引用,或者将引用传递到其他方法中。

所以,从对象发布的角度,我们将对象分为两种,一种是可以发布出去的,另外一种是不可以发布出去的。

不可以发布出去的对象,要确保该对象及其内部状态不能被发布。

可以发布出去的对象,在发布时要确保线程安全性,不能发布对象的内部状态,一旦对象的内部状态被发布出去了,那么就破坏了java语言的封装性。

在对象发布时,如果对象构造完成之前就将该对象发布,那么就会出现逸出,这也就是逸出的定义。

发布对象的2种情况

1. 将一个指向该对象的引用保存到其他代码可以访问的地方。发布一个对象,可能会间接地发布其他对象。就比如下面的代码:

public static Set<Secret> knownSecrets;

public void init() {
    knownSecrets = new HashSet<Secret>();
}

在一个类中,声明了一个公共的knownSecrets变量,这个变量是一个Secret的集合,这个knownSecrets也就相当于被发布出去了。所以外部的类,可以直接访问这个knownSecrets变量,而在初始化方法中,我们初始化了knownSecrets的值,这个值就是一系列的secret所构成的结合,所以相当于发布knownSecrets对象的时候,间接的发布了secret的对象。

2. 非私有的方法返回对象的引用

class UnsafeStates{
    private String[] states = new String[] {
        "AK", "AL";
    }
    
    public String[] getStates() {
        return states;
    }
}

上面的代码中,通过一个公共的方法返回states对象的引用,对外发布了该对象,但是任何调用者都能轻易的修改这个数组的内容,那么就可以说states数组的这个对象已经逸出了它所在的作用域,因为这个本该是私有的变量已经被发布了。

this引用逃逸的2种情况

1. 在构造函数中启动一个线程

当对象在其构造函数中创建一个线程时,this引用都会被新建的线程共享,所以不要在构造函数中启动他。

2. 在构造函数中使用监听器

public class ThisEscape {
    private final int var;
 
    public ThisEscape(EventSource source) {
        source.registerListener(
            new EventListener() {
                public void onEvent(Event e) {
                    doSomething(e);
                }
            }
        );
 
        // more initialization
        var = 10;
    }
 
    // result can be 0 or 10
    int doSomething(Event e) {
        return var;
    }
}

上述代码中初始化了事件监听器,事件监听器一旦注册成功,就能够监听用户的操作,调用对应的回调函数,比如监听到了e这个事件,那么就会执行doSomething()回调函数,函数中返回的是Thisescape的变量var,由于接受到监听事件是在构造函数中,所以很可能接受到事件时,var的值还没有在构造器中初始化完成,就会导致doSomething方法返回的值可能是0,也可能是10。

避免this引用逸出

上面,我们首先学习了定义,什么叫对象的发布和逸出,并且通过代码讲述了如何发布对象,进而使用代码描述了this引用逃逸。

所以,通过实践我们可以总结出this引用逃逸发生的两个必要条件

1. 在构造函数中创建内部类

2. 在构造函数中发布这个内部类

所以,破坏这两个条件就可以防止this引用逃逸

比如,如下代码,我们可以使用工厂方法来防止this引用逃逸在构造过程中逸出

public class SafeListener {

    private final EventListener listener;

    private SafeListener() {
        listener = new EventListener() {
            public void onEvent(Event e) {
                doSomething(e);
            }
        }
    }

    public static SafeListener newInstance(EventSource source) {
        
        SafeListener safe = new SafeListener();
        source.registerListener(safe.listener);
        return safe;
    }
}

 

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值