Java基础教程之this引用逃逸

1、什么是This逃逸?

  在构造器构造还未彻底完成前(即实例初始化阶段还未完成),将自身this引用向外抛出并被其他线程复制(访问)了该引用,可能会问到该还未被初始化的变量,甚至可能会造成更大严重的问题。

  废话不多说,看一下代码

/**
 * 模拟this逃逸
 * @author Lijian
 *
 */
public class ThisEscape {
    //final常量会保证在构造器内完成初始化(但是仅限于未发生this逃逸的情况下,具体可以看多线程对final保证可见性的实现)
    final int i;
    //尽管实例变量有初始值,但是还实例化完成
    int j = 0;
    static ThisEscape obj;
    public ThisEscape() {
        i=1;
        j=1;
        //将this逃逸抛出给线程B
        obj = new ThisEscape();
    }
    public static void main(String[] args) {
        //线程A:模拟构造器中this逃逸,将未构造完全对象引用抛出
        /*Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                //obj = new ThisEscape();
            }
        });*/
        //线程B:读取对象引用,访问i/j变量
        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
            31                //可能会发生初始化失败的情况解释:实例变量i的初始化被重排序到构造器外,此时1还未被初始化
                ThisEscape objB = obj;
                try {
                    System.out.println(objB.j);
                } catch (NullPointerException e) {
                    System.out.println("发生空指针错误:普通变量j未被初始化");
                }
                try {
                    System.out.println(objB.i);
                } catch (NullPointerException e) {
                    System.out.println("发生空指针错误:final变量i未被初始化");
                }
            }
        });
            //threadA.start();
            threadB.start();
    }
}

输出结果:这说明ThisEscape还未完成实例化,构造还未彻底结束。

发生空指针错误:普通变量j未被初始化
发生空指针错误:final变量i未被初始化

另一种情况是利用线程A模拟this逃逸,但不一定会发生,线程A模拟构造器正在构造...而线程B尝试访问变量,这是因为

(1)由于JVM的指令重排序存在,实例变量i的初始化被安排到构造器外(final可见性保证是final变量规定在构造器中完成的);

(2)类似于this逃逸,线程A中构造器构造还未完全完成。

所以尝试多次输出(相信我一定会发生的,只是概率相对低),也会发生类似this引用逃逸的情况。

/**
 * 模拟this逃逸
 * @author Lijian
 *
 */
public class ThisEscape {
    //final常量会保证在构造器内完成初始化(但是仅限于未发送this逃逸的情况下)
    final int i;
    //尽管实例变量有初始值,但是还实例化完成
    int j = 0;
    static ThisEscape obj;
    public ThisEscape() {
        i=1;
        j=1;
        //obj = new ThisEscape();
    }
    public static void main(String[] args) {
        //线程A:模拟构造器中this逃逸,将未构造完全对象引用抛出
        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                //构造初始化中...线程B可能获取到还未被初始化完成的变量
                //类似于this逃逸,但并不定发生
                obj = new ThisEscape();
            }
        });
        //线程B:读取对象引用,访问i/j变量
        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                //可能会发生初始化失败的情况解释:实例变量i的初始化被重排序到构造器外,此时1还未被初始化
                ThisEscape objB = obj;
                try {
                    System.out.println(objB.j);
                } catch (NullPointerException e) {
                    System.out.println("发生空指针错误:普通变量j未被初始化");
                }
                try {
                    System.out.println(objB.i);
                } catch (NullPointerException e) {
                    System.out.println("发生空指针错误:final变量i未被初始化");
                }
            }
        });
            threadA.start();
            threadB.start();
    }
}

2、什么情况下会This逃逸?

(1)在构造器中很明显地抛出this引用提供其他线程使用(如上述的明显将this抛出)。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/b0df48a1fc9d98504aa9a19fca089100.html