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

2023-06-09 08:42:43 浏览数 (2)

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

1、This逃逸中的关键词

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

    对象在发布时,应确保可发布对象线程安全;防止不可发布对象被发布出去,破坏面向对象中的密封性。

  • 逸出 对象逸出指对象在未完成构造时,对象被发布。

2、This逃逸示例分析

代码语言:javascript复制
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、如果避免发生逃逸

代码语言:javascript复制
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");     
   } 
 }

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

代码语言:javascript复制
static{
     source.registerListener(
          new EventListener() {
              public void onEvent(Event e) {
                  doSomething(e);
               }
           }
     );
     var = 10;
 }

5、总结

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

0 人点赞