JMM系列--綜述

其他網址

JMM概述_牧竹子-CSDN博客_jmm

java內存模型JMM理解整理 - 阿姆斯特朗迴旋炮 - 博客園

簡介

JMM是什麼

        JMM即爲JAVA 內存模型(java memory model)。因爲在不同的硬件生產商和不同的操作系統下,內存的訪問邏輯有一定的差異,結果就是當你的代碼在某個系統環境下運行良好,並且線程安全,但是換了個系統就出現各種問題。Java內存模型,就是爲了屏蔽系統和硬件的差異,讓一套代碼在不同平臺下能到達相同的訪問結果。JMM從java 5開始的JSR-133發佈後,已經成熟和完善起來。

什麼是內存模型?

內存模型描述了程序中各個變量(實例域、靜態域和數組元素)之間的關係,以及在實際計算機系統中將變量存儲到內存和從內存中取出變量這樣的底層細節

Java Memory Model(Java內存模型), 圍繞着在併發過程中如何處理可見性、原子性、有序性這三個特性而建立的模型。

JMM規定

  • 每個線程還有自己的工作內存(Working Memory),線程的工作內存中保存了該線程使用到的變量的主內存的副本拷貝,
  • 所有的變量都存儲在主內存(Main Memory)中。
  • 線程對變量的所有操作(讀取、賦值等)都必須在工作內存中進行,而不能直接讀寫主內存中的變量(首先要將變量從主內存拷貝到自己的工作內存空間,然後對變量進行操作,操作完成後在將變量寫回主內存)。(volatile變量仍然有工作內存的拷貝,但是由於它特殊的操作順序性規定,所以看起來如同直接在主內存中讀寫訪問一般)。
  • 不同的線程之間無法直接訪問對方工作內存中的變量,線程之間值的傳遞都需要通過主內存來完成。

JMM結構 

線程A與線程B之間如要通信的話,必須要經歷下面2個步驟:

  1. 線程A把本地內存A中更新過的共享變量刷新到主內存中去。
  2. 線程B到主內存中去讀取線程A之前已更新過的共享變量。

JMM三個特性

參考網址:java 程序中怎麼保證多線程的運行安全

JMM三個特性(線程安全性體現方面)

線程的安全性問題體現在:原子性、可見性、有序性。

體現方面

說明

導致原因

解決方法

原子性

一個或者多個操作在CPU執行的過程中不被中斷的特性

線程切換

JDK Atomic開頭的原子類(非阻塞CAS算法)

synchronized

LOCK

可見性

一個線程對共享變量的修改,另外一個線程能夠立刻看到

緩存

synchronized

volatile

LOCK

有序性

程序執行的順序按照代碼的先後順序執行

編譯優化

Happens-Before規則

Happens-Before規則

參考網址:java內存模型以及happens-before規則 - 簡書

1.規則項

規則

說明

程序順序規則 一個線程中的每個操作,happens-before於該線程中的任意後續操作
監視器鎖規則 對一個鎖的解鎖,happens-before於隨後對這個鎖的加鎖。

volatile變量規則

對一個volatile域的寫,happens-before於任意後續對這個volatile域的讀。
傳遞性規則 如果A happens-before B,且B happens-before C,那麼A happens-before C。
start()規則 如果線程A執行操作ThreadB.start()(啓動線程B),那麼A線程的ThreadB.start()操作happens-before於線程B中的任意操作。
join()規則 如果線程A執行操作ThreadB.join()併成功返回,那麼線程B中的任意操作happens-before於線程A從ThreadB.join()操作成功返回。

線程中斷規則

對線程interrupt()的調用 happen—before 發生於被中斷線程的代碼檢測到中斷時事件的發生。

對象終結規則

就是一個對象的初始化的完成(構造函數執行的結束) happens-before它的finalize()方法

2. happens-before含義

        JMM(java 內存模型)可以通過happens-before關係向程序員提供跨線程的內存可見性保證(如果A線程的寫操作a與B線程的讀操作b之間存在happens-before關係,儘管a操作和b操作在不同的線程中執行,但JMM向程序員保證a操作將對b操作可見)。具體的定義爲:

(1)如果一個操作happens-before另一個操作,那麼第一個操作的執行結果將對第二個操作可見,而且第一個操作的執行順序排在第二個操作之前。

(2)兩個操作之間存在happens-before關係,並不意味着Java平臺的具體實現必須要按照happens-before關係指定的順序來執行。如果重排序之後的執行結果,與按happens-before關係來執行的結果一致,那麼這種重排序並不非法(也就是說,JMM允許這種重排序)。

上面的(1)是JMM對程序員的承諾。從程序員的角度來說,可以這樣理解happens-before關係:如果A happens-before B,那麼Java內存模型將向程序員保證——A操作的結果將對B可見,且A的執行順序排在B之前。注意,這只是Java內存模型向程序員做出的保證!

上面的(2)是JMM對編譯器和處理器重排序的約束原則。正如前面所言,JMM其實是在遵循一個基本原則:只要不改變程序的執行結果(指的是單線程程序和正確同步的多線程程序),編譯器和處理器怎麼優化都行。JMM這麼做的原因是:程序員對於這兩個操作是否真的被重排序並不關心,程序員關心的是程序執行時的語義不能被改變(即執行結果不能被改變)。因此,happens-before關係本質上和as-if-serial語義是一回事。

3.happens-before存在的意義

        重排序原則有編譯器重排序和處理器重排序等,如果讓程序員再去了解這些底層的實現以及具體規則,那麼程序員的負擔就太重了,嚴重影響了併發編程的效率。因此,JMM爲程序員在上層提供了六條規則,這樣我們就可以根據規則去推論跨線程的內存可見性問題,而不用再去理解底層重排序的規則。

synchronized作用

(synchronized可保證原子性、有序性和可見性)

一個線程執行互斥代碼過程如下:

1. 獲得同步鎖;
2. 清空工作內存;
3. 從主內存拷貝對象副本到工作內存;
4. 執行代碼(計算或者輸出等);
5. 刷新主內存數據;
6. 釋放同步鎖。

 volatile作用

        保證有序性和可見性,不保證原子性。

可見性:一個變量被聲明爲volatile時,線程在寫入變量時不會把值緩存在寄存器或者其他地方,而是會把值刷新回主內存。當其他線程讀取該共享變量時,會從主內存重新獲取最新值,而不是使用當前線程的工作內存中的值。

有序性:通過禁止指令重排的方式保證有序性

volatile流程

1)從主存讀取volatile變量到本地副本
2)修改變量值
3)本地副本值寫回主存
4)插入內存屏障,即lock指令。內存屏障會讓其他線程每次讀取強制從主存讀取(讓其他線程可見)

 可見volatile並沒有加鎖,1234不是原子性的。

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