JMM(Java內存模型)---線程安全極概念淺析雜記

由於CPU有多級緩存 處理器爲了提高運算速度,做出了違背代碼原有順序在多核時代多線程下使得計算不準確會發生。

Java虛擬機提供了一套統一的標準讓不同的虛擬機,不同的CPU上代碼運行結果一致,這個標準就是Java內存模型

(Java Memory Model,簡稱JMM),它規定了一個線程如何和何時可以看到其他線程對共享變量的修改以及在必要的時候如何同步訪問共享變量。

Java內存模型的有8種操作:

1.鎖定(Lock):作用於主存中的變量,把一個變量標識成一個線程獨佔狀態。

2.解鎖(Unlock):作用於主存中的變量,把一個處於鎖定狀態的變量釋放出來,此變量才能被其他線程鎖定。

3.讀(Read):作用於主存中的變量,把一個變量值從主存中傳遞到線程的工作內存中,以便於隨後的Load使用。

4.載入(Load):作用於工作內存中的變量,將read操作從主存中得到的變量放入工作內存的變量副本中。

5.使用(Use):作用於工作內存中的變量,把工作內存中的一個變量的值傳遞給執行引擎。

6.賦值(Assign):作用於工作內存中的變量,將從執行引擎接收到的值賦值給工作內存中變量。

7.存儲(Store):作用於工作內存中的變量,將工作內存的一個變量的值傳送主存中,以便於隨後的write操作。

8.寫入(Write):作用於主存中的變量,將store操作從工作內存中的一個變量值傳遞到主存的變量中。

對應的同步規則:

如果要把一個變量從主存複製值工作內存中,就需要按順序地執行read和load操作,如果把變量從工作內存同步到主存中,就要按順序的store和write操作,但是Java內存模型只要求上述操作必須是按順序執行,沒有保證必須是連續執行。

不允許read和load、store和write操作之一單獨出現,即他們都是成對出現的

不允許一個線程丟棄它最近的assign操作,即變量在工作內存中改變了之後必須同步到主存中

不允許一個線程無原因的(沒有發生任何assign操作)把數據從工作內存同不回主存中

一個新的變量只能在主存中誕生,不允許在工作內存中直接使用一個未被初始化(load或assign)的變量。即對一個變量實施use和store操作之前,必須對其執行了assign和load操作

一個變量在同一時刻只允許一個線程對其進行lock操作,但lock操作可以被同一線程重複執行多次,多次執行lock後,只有執行了相同次數的unlock操作後變量纔會被解鎖,lock和unlock必須成對出現

如果對一個變量執行lock操作.將會清空工作內存中此變量的值,在執行引擎使用這個變量前要重新執行load和assign操作來初始化變量的值

如果一個變量沒有被lock操作鎖定,則不允許對他執行unlock操作;也不允許去unlock一個被其他線程鎖定的變量

對一個變量執行unlock操作前,必須先把變量同步到主存中(store和write操作)

 

JMM將內存分爲堆和棧

:主要是存放基本類型變量,堆地址的引用(對象句柄),數據是可以共享的,速度快,大小和生存期是確定的,JMM要求調用棧和本地變量存放在線程棧上

:運行是數據區,動態分配的,GC自動回收不可達的數據,速度慢

 堆在JDK1.8之前分爲年輕代和老年代,JDK 1.8 +(含)老年代變成元空間了不再堆內存中保存,直接存儲在主存中了。年輕代又分爲伊甸園區和存活區

不同的線程訪問同一個對象的方法對的屬性,能訪問到的都是該對象的私有拷貝。

JMM中是允許編譯器對指令進行重排序的,這些重排序不會影響單線程運行,卻會影響到多線程的併發執行。

線程安全:

 當多個線程訪問某個類時,不管運行時環境採用何種調度方式或者這些線程是如何交替執行,並且不需要在主線程中進行任何額外的同步或者協同,這個類都能表現出正確的期待結果,那麼這個類就是線程安全的。

線程安全主要體現在三個方面:

原子性:提供了互斥訪問,同一時刻只能有一個線程對其進行操作,能保證原子性的方式 :JDK atomic包(採用CAS算法實現),synchronized,lock

可見性:一個線程對主內存的修改可以及時的被其他線程觀察到 保可見性:volatitle、synchronized

有序性:一個線程觀察其他線程中的指令的執行順序,由於指令重排存在,該觀察結果一般是雜亂無序的

有序性(hapeens-before原則)

1.程序次序原則:一個線程內,代碼會按照書寫順序執行,即代碼按行依次執行。(JVM對指令重排序時候會不存數據依賴的進行指令重排序)

2.鎖定規則:一個unlock操作一定發生在後續對同一個鎖lock操作之前。

3,volatile變量規則:同一個變量的寫一定發生在讀之前。

4.傳遞規則:A操作發生在B操作之前,B發生在C之前 ,則A發生在C之前

5.線程啓動原則:Thread對象的start()方法先行發生在此線程的每一個動作。

6.線程中斷原則:對線程interrupt()方法調用先行於被中斷線程的代碼檢測到中斷事件的發生

7.線程終結規則:線程中所有的操作一定在線程終止檢測之前。

8.對象終極規則:對象的初始化操作一定在finalize()方法之前。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章