Java多线程--内存模型(JMM)--详解
原文網址:Java多線程--內存模型(JMM)--詳解_IT利刃出鞘的博客-CSDN博客
簡介
? ? ? ? 本文介紹Java的內存模型(JMM)。包括:JMM是什么,JMM結構,JMM的特性(線程安全型體現在哪些方面),synchronized的作用,volatile的作用。
JMM是什么
? ? ? ? JMM即為JAVA 內存模型(java memory model)。因為在不同的硬件生產商和不同的操作系統下,內存的訪問邏輯有一定的差異,結果就是當你的代碼在某個系統環境下運行良好,并且線程安全,但是換了個系統就出現各種問題。Java內存模型,就是為了屏蔽系統和硬件的差異,讓一套代碼在不同平臺下能到達相同的訪問結果。JMM從java 5開始的JSR-133發布后,已經成熟和完善起來。
? ? ? ? 內存模型描述了程序中各個變量(實例域、靜態域和數組元素)之間的關系,以及在實際計算機系統中將變量存儲到內存和從內存中取出變量這樣的底層細節
? ? ? ? Java Memory Model(Java內存模型), 圍繞著在并發過程中如何處理可見性、原子性、有序性這三個特性而建立的模型。
JMM規定
- 所有的變量都存儲在主內存(Main Memory)中。
- 每個線程有自己的工作內存(Working Memory),線程的工作內存中保存了該線程使用到的變量的主內存的副本拷貝,
- 線程對變量的所有操作(讀取、賦值等)都必須在工作內存中進行,而不能直接讀寫主內存中的變量(首先要將變量從主內存拷貝到自己的工作內存空間,然后對變量進行操作,操作完成后在將變量寫回主內存)。(volatile變量仍然有工作內存的拷貝,但是由于它特殊的操作順序性規定,所以看起來如同直接在主內存中讀寫訪問一般)。
- 不同的線程之間無法直接訪問對方工作內存中的變量,線程之間值的傳遞都需要通過主內存來完成。
JMM結構
線程A與線程B之間如要通信的話,必須要經歷下面2個步驟:
JMM三個特性
JMM三個特性(線程安全性體現方面)
線程的安全性問題體現在:原子性、可見性、有序性。
| 體現方面 | 說明 | 導致原因 | 解決方法 |
| 原子性 | 一個或者多個操作在CPU執行的過程中不被中斷的特性 | 線程切換 | synchronized LOCK JDK?Atomic開頭的原子類(非阻塞CAS算法) |
| 可見性 | 一個線程對共享變量的修改,另外一個線程能夠立刻看到 | 緩存 | synchronized LOCK volatile |
| 有序性 | 程序執行的順序按照代碼的先后順序執行 | 編譯優化 | 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不是原子性的。
其他網址
JMM概述_牧竹子-CSDN博客_jmm
java內存模型JMM理解整理 - 阿姆斯特朗回旋炮 - 博客園
java 程序中怎么保證多線程的運行安全
總結
以上是生活随笔為你收集整理的Java多线程--内存模型(JMM)--详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 作业5-需求分析(EX:南通大学成绩录入
- 下一篇: java美元兑换,(Java实现) 美元