<form id="dlljd"></form>
        <address id="dlljd"><address id="dlljd"><listing id="dlljd"></listing></address></address>

        <em id="dlljd"><form id="dlljd"></form></em>

          <address id="dlljd"></address>
            <noframes id="dlljd">

              聯系我們 - 廣告服務 - 聯系電話:
              您的當前位置: > 關注 > > 正文

              java內存模型——volatile的可見性

              來源:CSDN 時間:2023-03-17 10:27:33

              volatile的使用與線程安全關系密切,主要作用是使變量在多個線程間可見,另外也有防止指令重排的作用。


              (相關資料圖)

              比如主內存中有變量a=0,線程1設置a=10,線程2再操作a的時候,是以a=10的基礎上進行操作,否則會影響邏輯!

              0 1volatile的可見性

              要了解volatile的可見性,首先得了解java內存模型:

              java內存模型

              Java內存模型由Java虛擬機規范定義,用來屏蔽各個平臺的硬件差異。簡單來說:

              1. 所有變量儲存在主內存。

              2. 每條線程擁有自己的工作內存,其中保存了主內存中線程使用到的變量的副本。

              3. 線程不能直接讀寫主內存中的變量,所有操作均在工作內存中完成。

              線程,主內存,工作內存的交互關系如下圖所示

              如下列代碼所示,rt啟動之后修改isRunning的值為false,此時while循環不會停止,因為run方法里得不到改變之后的isRunning。

              解決:使用volatile修飾isRunning,這樣當isRunning的值改變之后,會立即刷新到主內存里,工作內存也能立即獲取到新的值

              public class RunThread extends Thread {private boolean isRunning = true;  private void setRunning(boolean isRunning){this.isRunning = isRunning;  }    public void run () {System.out.println("進入run方法");    while(isRunning == true){//...    }    System.out.println("線程停止");  }  public static void main(String[] args) {RunThread rt = new RunThread();    rt.start();    try {Thread.sleep(3000);      rt.setRunning(false);      System.out.println("isRunning的值已經被設置成false");      Thread.sleep(1000);      System.out.println(rt.isRunning);    } catch (InterruptedException e) {// TODO Auto-generated catch block      e.printStackTrace();    }  }}

              0 2volatile能防止指令重排

              如下列代碼所示,這是單例模式的雙檢鎖寫法

              public class SingletonTest {private volatile static SingletonTest instance = null;    private SingletonTest() { }    public static SingletonTest getInstance() {if(instance == null) {synchronized (SingletonTest.class){if(instance == null) {instance = new SingletonTest();  //非原子操作                }            }        }        return instance;    }}

              我們看到instance用了volatile修飾,由于 instance = new SingletonTest();可分解為:

              1.memory =allocate(); //分配對象的內存空間2.ctorInstance(memory); //初始化對象3.instance =memory; //設置instance指向剛分配的內存地址

              操作2依賴1,但是操作3不依賴2,所以有可能出現1,3,2的順序,當出現這種順序的時候,雖然instance不為空,但是對象也有可能沒有正確初始化,會出錯。

              而使用volatile修飾instance之后,不會出現亂序的行為!

              0 3volatile不保證原子性以及解決方式

              1.什么是原子性?

              下列語句中,哪些是原子性操作?

              x = 10;         //語句1y = x;          //語句2x++;            //語句3x = x + 1;      //語句4

              語句1 是直接將數值 10 賦值給 x,也就是說線程執行這個語句的會直接將數值 10 寫入到工作內存中;

              語句2 實際上包含兩個操作,它先要去讀取 x 的值,再將 x 的值寫入工作內存。雖然,讀取 x 的值以及 將 x 的值寫入工作內存這兩個操作都是原子性操作,但是合起來就不是原子性操作了;

              同樣的,x++ 和 x = x+1 包括3個操作:讀取 x 的值,進行加 1 操作,寫入新的值。

              只有 語句1 的操作具備原子性。也就是說,只有簡單的讀取、賦值(而且必須是將數字賦值給某個變量,變量之間的相互賦值不是原子操作)才是原子操作!

              2.舉例

              如下列代碼所示,使用volatile修飾的變量count,利用10個線程分別對count進行++操作,而根據上面的表述,++操作不是原子操作!每個線程加1000個數,打印的結果中一定有一個是10000才是對的,但是實際上并不是這樣!因為volatile不保證原子性!

              public class VolatileNoAtomic extends Thread{private static volatile int count;//  private static AtomicInteger count = new AtomicInteger(0);  private static void addCount(){for (int i = 0; i < 1000; i++) {count ++;//      count.incrementAndGet();    }    System.out.println(count);  }  public void run(){addCount();  }    public static void main(String[] args) {VolatileNoAtomic[] arr = new VolatileNoAtomic[10];    for (int i = 0; i < arr.length; i++) {arr[i] = new VolatileNoAtomic();    }        for (int i = 0; i < arr.length; i++) {arr[i].start();    }  }}

              解決:

              方法1:使用原子類Atomic類的系列對象,這樣既不會阻塞,又能保證原子性!

              方法2:使用synchronized修飾addCount方法,這樣做的話,線程同步之后會有阻塞,運行時間加長,而且volatile將會失效,不建議這么改

              方法3:使用Lock加鎖,當然,跟方法2一樣的有阻塞

              ● 架構系列——使用synchronized需要注意什么細節

              ● 架構系列——線程實現方式以及生命周期的探索

              ● 架構系列——并發、并行與多線程關系探索

              ● 架構系列——單體、分布式、集群與冗余的探索

              ● Java反射:框架設計的靈魂

              ● 高并發秒殺系統如何設計與優化

              ● 要準備多少東西去面試---java中高級面試總結(值得收藏)

              ● 最近的面試有感(7個方面)

              ● java中的參數傳遞(只有值傳遞沒有引用傳遞)

              ● 38張史上最全的IT工程師技能圖譜(高清收藏)

              ● PLSQL連接本地oracle或遠程oracle數據庫,實現隨意切換(送福利)

              ● 通過數據泵expdp、impdp方式備份與還原Oracle數據庫--值得收藏

              ● java常見排序算法--選擇排序、冒泡排序、插入排序分析與比較

              責任編輯:

              標簽:

              相關推薦:

              精彩放送:

              新聞聚焦
              Top 中文字幕在线观看亚洲日韩