亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

java this引用逃逸詳解

 更新時間:2020年12月01日 09:42:01   作者:Jian  
這篇文章主要介紹了java this引用逃逸的相關資料,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下

1、什么是This逃逸?

  在構造器構造還未徹底完成前(即實例初始化階段還未完成),將自身this引用向外拋出并被其他線程復制(訪問)了該引用,可能會問到該還未被初始化的變量,甚至可能會造成更大嚴重的問題。

  廢話不多說,看一下代碼

 /**
  * 模擬this逃逸
  * @author Lijian
  *
  */
 public class ThisEscape {
   //final常量會保證在構造器內完成初始化(但是僅限于未發(fā)生this逃逸的情況下,具體可以看多線程對final保證可見性的實現(xiàn))
   final int i;
   //盡管實例變量有初始值,但是還實例化完成
   int j = 0;
   static ThisEscape obj;
   public ThisEscape() {
     i=1;
     j=1;
     //將this逃逸拋出給線程B
     obj = this;
   }
   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() {
               //可能會發(fā)生初始化失敗的情況解釋:實例變量i的初始化被重排序到構造器外,此時1還未被初始化
         ThisEscape objB = obj;
         try {
           System.out.println(objB.j);
         } catch (NullPointerException e) {
           System.out.println("發(fā)生空指針錯誤:普通變量j未被初始化");
         }
         try {
           System.out.println(objB.i);
         } catch (NullPointerException e) {
           System.out.println("發(fā)生空指針錯誤:final變量i未被初始化");
         }
       }
     });
       //threadA.start();
       threadB.start();
   }
 }

輸出結果:這說明ThisEscape還未完成實例化,構造還未徹底結束。

發(fā)生空指針錯誤:普通變量j未被初始化
發(fā)生空指針錯誤:final變量i未被初始化

另一種情況是利用線程A模擬this逃逸,但不一定會發(fā)生,線程A模擬構造器正在構造...而線程B嘗試訪問變量,這是因為

(1)由于JVM的指令重排序存在,實例變量i的初始化被安排到構造器外(final可見性保證是final變量規(guī)定在構造器中完成的);

(2)類似于this逃逸,線程A中構造器構造還未完全完成。

所以嘗試多次輸出(相信我一定會發(fā)生的,只是概率相對低),也會發(fā)生類似this引用逃逸的情況。

/**
 * 模擬this逃逸
 * @author Lijian
 *
 */
public class ThisEscape {
  //final常量會保證在構造器內完成初始化(但是僅限于未發(fā)送this逃逸的情況下)
  final int i;
  //盡管實例變量有初始值,但是還實例化完成
  int j = 0;
  static ThisEscape obj;
  public ThisEscape() {
    i=1;
    j=1;
    //obj = this ;
  }
  public static void main(String[] args) {
    //線程A:模擬構造器中this逃逸,將未構造完全對象引用拋出
    Thread threadA = new Thread(new Runnable() {
      @Override
      public void run() {
        //構造初始化中...線程B可能獲取到還未被初始化完成的變量
        //類似于this逃逸,但并不定發(fā)生
        obj = new ThisEscape();
      }
    });
    //線程B:讀取對象引用,訪問i/j變量
    Thread threadB = new Thread(new Runnable() {
      @Override
      public void run() {
        //可能會發(fā)生初始化失敗的情況解釋:實例變量i的初始化被重排序到構造器外,此時1還未被初始化
        ThisEscape objB = obj;
        try {
          System.out.println(objB.j);
        } catch (NullPointerException e) {
          System.out.println("發(fā)生空指針錯誤:普通變量j未被初始化");
        }
        try {
          System.out.println(objB.i);
        } catch (NullPointerException e) {
          System.out.println("發(fā)生空指針錯誤:final變量i未被初始化");
        }
      }
    });
      threadA.start();
      threadB.start();
  }
}

2、什么情況下會This逃逸?

(1)在構造器中很明顯地拋出this引用提供其他線程使用(如上述的明顯將this拋出)。

(2)在構造器中內部類使用外部類情況:內部類訪問外部類是沒有任何條件的,也不要任何代價,也就造成了當外部類還未初始化完成的時候,內部類就嘗試獲取為初始化完成的變量

  • 在構造器中啟動線程:啟動的線程任務是內部類,在內部類中xxx.this訪問了外部類實例,就會發(fā)生訪問到還未初始化完成的變量
  • 在構造器中注冊事件,這是因為在構造器中監(jiān)聽事件是有回調函數(shù)(可能訪問了操作了實例變量),而事件監(jiān)聽一般都是異步的。在還未初始化完成之前就可能發(fā)生回調訪問了未初始化的變量。

在構造器中啟動線程代碼實現(xiàn):

/**
 * 模擬this逃逸2:構造器中啟動線程
 * @author Lijian
 *
 */
public class ThisEscape2 {
  final int i;
  int j;
  public ThisEscape2() {
    i = 1;
    j = 1;
    new Thread(new RunablTest()).start();
  }
  //內部類實現(xiàn)Runnable:引用外部類
  private class RunablTest implements Runnable{
    @Override
    public void run() {
      try {
        System.out.println(ThisEscape2.this.j);
      } catch (NullPointerException e) {
        System.out.println("發(fā)生空指針錯誤:普通變量j未被初始化");
      }
      try {
        System.out.println(ThisEscape2.this.i);
      } catch (NullPointerException e) {
        System.out.println("發(fā)生空指針錯誤:final變量i未被初始化");
      }
    }

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

構造器中注冊事件,引用網(wǎng)上的一段偽代碼將以解釋:

public class ThisEscape3 {
  private final int var;
 
  public ThisEscape3(EventSource source) {     //注冊事件,會一直監(jiān)聽,當發(fā)生事件e時,會執(zhí)行回調函數(shù)doSomething
    source.registerListener(       //匿名內部類實現(xiàn)
      new EventListener() {
        public void onEvent(Event e) {            //此時ThisEscape3可能還未初始化完成,var可能還未被賦值,自然就發(fā)生嚴重錯誤
          doSomething(e);
        }
      }
    );
    var = 10;
  }
  // 在回調函數(shù)中訪問變量
  int doSomething(Event e) {
    return var;
  }
}

3、怎樣避免This逃逸?
  (1)單獨編寫一個啟動線程的方法,不要在構造器中啟動線程,嘗試在外部啟動。

...
private Thread t;
public ThisEscape2() {
  t = new Thread(new EscapeRunnable());
}

public void initStart() {
  t.start();
}
...

  (2)將事件監(jiān)聽放置于構造器外,比如new Object()的時候就啟動事件監(jiān)聽,但是在構造器內不能使用事件監(jiān)聽,那可以在static{}中加事件監(jiān)聽,這樣就跟構造器解耦了

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

4、總結

  this引用逃逸問題實則是Java多線程編程中需要注意的問題,引起逃逸的原因無非就是在多線程的編程中“濫用”引用(往往涉及構造器中顯式或隱式地濫用this引用),在使用到this引用的時候需要特別注意!

  同時這會涉及到:final的內存語義,即final域禁止重排序問題(2020.11.22增加),包括寫final域與讀final域重排序兩個規(guī)則(參考資料《Java并發(fā)編程的藝術》)

以上就是java this引用逃逸詳解的詳細內容,更多關于java this引用逃逸的資料請關注腳本之家其它相關文章!

相關文章

  • 利用Java實現(xiàn)簡單的猜數(shù)字小游戲

    利用Java實現(xiàn)簡單的猜數(shù)字小游戲

    這篇文章主要為大家詳細介紹了如何利用java語言實現(xiàn)猜數(shù)字小游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • 解讀@Transactional失效的幾種情況

    解讀@Transactional失效的幾種情況

    這篇文章主要介紹了@Transactional失效的幾種情況,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • springboot?使用?minio的示例代碼

    springboot?使用?minio的示例代碼

    Minio是Apcche旗下的一款開源的輕量級文件服務器,基于對象存儲,協(xié)議是基于Apache?License?v2.0,開源可用于商務,本文給大家介紹下springboot?使用?minio的示例代碼,感興趣的朋友看看吧
    2022-03-03
  • 使用java實現(xiàn)各種數(shù)據(jù)統(tǒng)計圖(柱形圖,餅圖,折線圖)

    使用java實現(xiàn)各種數(shù)據(jù)統(tǒng)計圖(柱形圖,餅圖,折線圖)

    用Jfree實現(xiàn)條形柱狀圖表,java代碼實現(xiàn)??山?jīng)常用于報表的制作,代碼自動生成后可以自由查看??梢宰杂膳渲脠D表的各個屬性,用來達到自己的要求和目的。本文給大家介紹使用java實現(xiàn)各種數(shù)據(jù)統(tǒng)計圖(柱形圖,餅圖,折線圖),需要的朋友可以參考下
    2015-10-10
  • Mybatis Plugin攔截器開發(fā)過程詳解

    Mybatis Plugin攔截器開發(fā)過程詳解

    這篇文章主要介紹了Mybatis Plugin攔截器開發(fā)過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-02-02
  • SpringCloud OpenFeign概述與使用

    SpringCloud OpenFeign概述與使用

    OpenFeign源于Netflix的Feign,是http通信的客戶端。屏蔽了網(wǎng)絡通信的細節(jié),直接面向接口的方式開發(fā),讓開發(fā)者感知不到網(wǎng)絡通信細節(jié)。所有遠程調用,都像調用本地方法一樣完成
    2023-01-01
  • Java集合類的組織結構和繼承、實現(xiàn)關系詳解

    Java集合類的組織結構和繼承、實現(xiàn)關系詳解

    這篇文章主要介紹了Java集合類的組織結構和繼承、實現(xiàn)關系,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • 詳解Java基礎知識——JDBC

    詳解Java基礎知識——JDBC

    這篇文章主要介紹了Java基礎知識——JDBC,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-04-04
  • RabbitMQ的安裝和配置可視化界面的詳細步驟

    RabbitMQ的安裝和配置可視化界面的詳細步驟

    這篇文章主要介紹了RabbitMQ的安裝和配置可視化界面的詳細步驟,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-06-06
  • MyBatis延遲加載策略深入探究

    MyBatis延遲加載策略深入探究

    本文主要為大家詳細介紹下mybatis的延遲加載,從原理上介紹下怎么使用、有什么好處能規(guī)避什么問題。感興趣的小伙伴可以跟隨小編一起學習一下
    2022-07-07

最新評論