日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java异步线程内存可见性实验

發布時間:2023/12/3 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java异步线程内存可见性实验 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

【README】

本文演示了內存可見性的場景,以及解決方法;

相關定義如下(轉自java并發編程實戰,一本好書,強烈推薦):

  • 內存可見性一個線程修改了對象狀態后, 其他線程可以看到修改后的結果;
  • 對象發布:使對象能夠在當前作用域之外的代碼中使用;?
  • 對象逸出:當某個不應該發布的對象被發布時;

  • 【1】內存可見性問題例子

    【1.1】測試用例

    public class TestVisibility {public static void main(String[] args) {Robot robot = new Robot();new Thread(()->{try {// 睡眠模擬業務邏輯處理TimeUnit.MILLISECONDS.sleep(1000);} catch (InterruptedException e) {}robot.init();}).start();System.out.println(robot.getAge());int count = 0;while(robot.getAge() == 0) {++count;}System.out.println("主線程【成功】讀取子線程設置的age值");} } /*** @description 機器人* @author xiao tang* @date 2022/2/13*/ class Robot {int age = 0;public void init() {this.age = 10;}public int getAge() {return this.age;} }

    打印結果:

    0

    while進入了死循環

    【1.2】 分析

    子線程睡眠一秒,使得主線程可以先從主存(內存)讀取到age的值,并放入自己的緩存;

    然后子線程再更新age的值并同步到主存;

    由于主線程不從主存讀取age,所以它使用的是age的失效值(臟數據);


    【2】解決方法

    1)方法1, 為 Robot.age 添加 volative 修飾符; 以保證age的內存可見性;

    2)方法2,為 getAge() 方法 添加 synchronized 修飾符,以保證age的內存可見性;

    3)方法3,while循環體,即++count下面添加一條 打印語句,如下:

    while(robot.getAge() == 0) {++count;System.out.println(count);}

    為什么添加 System.out.println(count); 后就可以解決內存可見性問題了?

    因為 pringln 內部是 synchronized 代碼塊來實現的;如下:

    public void println(int x) {synchronized (this) {print(x);newLine();}}

    方法3與方法2的原理一樣,借助 synchronized 能夠保證內存可見性的原理;?


    【3】補充

    1,對于睡眠語句, TimeUnit.MILLISECONDS.sleep(1000); 我們可以測試出 內存可見性效果;

    但是,如果把 1000 換為 1 或 10, 500, ...... 會產生不同的效果,這就類似于不同業務邏輯有著不同的耗時; 即 內存可見性問題會因為不同的耗時而表現出不同的結果

    這就很麻煩了,因為如果業務邏輯真正存在可見性問題,但可能沒有測試出來,但生產上又存在發生可能性;

    我們唯一需要做的就是,在多線程編程時,保證內存可見性,安全的發布共享對象;


    【3.1】 happers-before

    以下內容轉自 :

    關于Java內存可見性的探究實驗遇到的意外和happens-before - 簡書

    happens-before字面翻譯過來就是先行發生,A happens-before B 就是A先行發生于B?

    不準確!在Java內存模型中,happens-before
    應該翻譯成:前一個操作的結果可以被后續的操作獲取。講白點就是前面一個操作把變量a賦值為1,那后面一個操作肯定能知道a已經變成了1。

    我們再來看看為什么需要這幾條規則?

    因為我們現在電腦都是多CPU,并且都有緩存,導致多線程直接的可見性問題。

    所以為了解決多線程的可見性問題,就搞出了happens-before原則,讓線程之間遵守這些原則。編譯器還會優化我們的語句,所以等于是給了編譯器優化的約束。不能讓它優化的不知道東南西北了!。

    1)關于 happens-before,有以下8條規則:

  • 單線程Happens-Before原則:在同一個線程中,書寫在前面的操作happen-before后面的操作。
  • 鎖的Happens-Before原則:同一個鎖的unlock操作happen-before此鎖的lock操作。(本文中的 System.out.println中的 synchronized 就是java內置鎖
  • volatile的Happens-Before原則:對一個volatile變量的寫操作happen-before對此變量的任意操作(當然也包括寫操作了)。
  • Happens-Before的傳遞性原則:如果A操作 happen-before B操作,B操作happen-before C操作,那么A操作happen-before C操作。
  • 線程啟動的Happens-Before原則:同一個線程的start方法happen-before此線程的其它方法。
  • 線程中斷的Happens-Before原則:對線程interrupt方法的調用happen-before被中斷線程的檢測到中斷發送的代碼。
  • 線程終結的Happens-Before原則:線程中的所有操作都happen-before線程的終止檢測。
  • 對象創建的Happens-Before原則:一個對象的初始化完成先于他的finalize方法調用。
  • 詳細請看:https://segmentfault.com/a/1190000011458941

    2)本文中鎖的happens-before的作用:

    由于happens-before原則,在獲取鎖時,主線程會使自己CPU的緩存失效,重新從主內存中讀取變量的值。這樣,子線程中的操作結果就會被主線程感知到了,從主內存中獲取了最新的a值。

    總結

    以上是生活随笔為你收集整理的java异步线程内存可见性实验的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。