【Java基础】This逃逸(This引用逃逸详解)

简介: 【Java基础】This逃逸(This引用逃逸详解)

【Java基础】This逃逸( This引用逃逸详解)

1、This逃逸中的关键词

  • This引用逃逸
    对象在还没有构造完成时,This引用已经发布出。
  • 发布对象发布是指一个对象在作用域范围之外被使用。如将一个指向该对象的引用保存到其他代码可以访问的地方,或在一个非私有的方法中返回该对象引用,或者将引用传递到其他方法中此类操作叫对象发布。一般讲来说对象发布分两种:
  1. 可以发布出去对象;
  2. 不可以发布出去对象;
  • 对象在发布时,应确保可发布对象线程安全;防止不可发布对象被发布出去,破坏面向对象中的密封性。
  • 逸出
    对象逸出指对象在未完成构造时,对象被发布。

2、This逃逸示例分析

public class ThisEscape {
    final  int a;
    int b=0;
    static ThisEscape obj;
    public ThisEscape(){
       a=1;
       b=1;
       obj=this;
    }
    public static void main(String[] args) {
        /**
         * 线程A:模拟构造器中this逃逸,将未构造完全对象引用抛出
         */
        Thread threadA =new Thread(new Runnable() {
            @Override
            public void run() {
                obj=new ThisEscape();
            }
        });
        /**
         * 线程B:读取对象引用,访问a/b变量
         */
        Thread threadB=new Thread(new Runnable() {
            @Override
            public void run() {
                ThisEscape objA = obj;
                try {
                    System.out.println(objA.b);
                }catch (NullPointerException e){
                    System.out.println("发生空指针错误:普通变量b未被初始化");
                }
                try {
                    System.out.println(objA.a);
                } catch (NullPointerException e) {
                    System.out.println("发生空指针错误:final变量a未被初始化");
                }
            }
        });
        threadB.start();
    }
}

运行结果:

线程A模拟this逃逸,但是不一定发生,而线程B发生this逃逸这是因为:

  1. 由于JVM的指令重排序存在,实例变量i的初始化被安排到构造器外(final可见性保证是final变量规定在构造器中完成的)
  2. 类似于this逃逸,线程A中构造器构造还未完全完成。

3、什么情况下会This逃逸

发生This逃逸一般会有两种情况:

  1. 在构造器中启动线程:启动的线程任务是内部类,在内部类中 xxx.this 访问了外部类实例,就会发生访问到还未初始化完成的变量
  2. 在构造器中注册事件,这是因为在构造器中监听事件是有回调函数(可能访问了操作了实例变量),而事件监听一般都是异步的。在还未初始化完成之前就可能发生回调访问了未初始化的变量。

4、如果避免发生逃逸

1.单独编写一个启动线程的方法,不要在构造器中启动线程,尝试在外部启动。

public class EventHandle01 implements Runnable {
    private Thread threadA=null;
    public EventHandle01() {
        threadA = new Thread(new EventHandle01());
    }
    public void initStart() {
        threadA.start();
    }
    @Override
    public void run() {
        System.out.println("This is the run method");
    }
}

2.将事件监听放置于构造器外,比如new Object()的时候就启动事件监听,但是在构造器内不能使用事件监听,那可以在static{}中加事件监听,这样就跟构造器解耦了

static{
     source.registerListener(
          new EventListener() {
              public void onEvent(Event e) {
                  doSomething(e);
               }
           }
     );
     var = 10;
 }

5、总结

This逃逸一般发生在多线程中,引起This逃逸的问题是在多线程滥用引用。在多线程中,使用到引用的时候多加注意.



目录
相关文章
|
Java
Java中的this关键字
Java中的this关键字
124 1
|
存储 Java
滚雪球学Java(41):Lambda表达式和方法引用:提高代码可读性和简洁性的神器
【5月更文挑战第16天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
136 2
滚雪球学Java(41):Lambda表达式和方法引用:提高代码可读性和简洁性的神器
|
缓存 Java 数据库连接
java面试题目 强引用、软引用、弱引用、幻象引用有什么区别?具体使用场景是什么?
【6月更文挑战第28天】在 Java 中,理解和正确使用各种引用类型(强引用、软引用、弱引用、幻象引用)对有效的内存管理和垃圾回收至关重要。下面我们详细解读这些引用类型的区别及其具体使用场景。
224 3
java方法引用::
java方法引用::
Java的this关键字的使用
Java的this关键字的使用
158 3
|
Java 运维
开发与运维引用问题之软引用又在Java特点如何解决
开发与运维引用问题之软引用又在Java特点如何解决
98 0
|
存储 Java 索引
Java一维数组元素的引用与操作技术详解
Java一维数组元素的引用与操作技术详解
180 1
|
Java 数据安全/隐私保护
Java基础手册二(类和对象 对象创建和使用 面向对象封装性 构造方法与参数传递 this关键字 static关键字 继承 多态 方法覆盖 final关键字 访问控制权限修饰符)
Java基础手册二(类和对象 对象创建和使用 面向对象封装性 构造方法与参数传递 this关键字 static关键字 继承 多态 方法覆盖 final关键字 访问控制权限修饰符)
117 0
|
Java UED 开发者
JVM逃逸分析原理解析:优化Java程序性能和内存利用效率
JVM逃逸分析原理解析:优化Java程序性能和内存利用效率
|
存储 Java 索引
Java的数组定义与引用
Java的数组定义与引用
113 0