【Java貓說】Java多線程以內存可見性(上篇)

閱讀本文約「3分鐘」segmentfault


本文大體講述兩種線程實現的可見性,或許你已經提早想到了,那說明你的基礎很好,咱們要聊聊synchronized實現可見性與volatile實現可見性。多線程

咱們會談及幾個點:指令重排序、as-if-serial語義、volatile使用注意事項等spa

首先咱們要了解下兩個名詞,有點術語的感受,雖然我不喜歡那些專業名詞,可是你懂得···線程

可見性:通俗的說就是一個線程對共享變量值的修改,能夠及時地被其它線程看到
共享變量:即一個變量在多個線程的工做內存中存在副本,則這個變量就是這些線程的共享變量排序

這兩個名稱理解起來還不算難,對吧?那麼咱們來看看更加專業化的名詞,我其實更但願有具象化的有趣的名詞來代替,原諒個人文學水平有限。圖片

Java內存模型(JMM)內存

Java Memory Model 描述了Java程序中各類變量(這裏指線程共享變量,你已經理解上面的第二個名詞了)的訪問規則,以及在JVM中將變量存儲到內存和從內存中讀取出變量這樣的底層細節,就像細胞要在血管中流動同樣(一個不及格的比喻),它要求全部的變量都存儲在主內存中,每一個線程都有本身獨立的工做內存,裏面保存該線程使用到的變量的副本(也就是主內存中該變量的一份拷貝)get

讓咱們來看看圖型吧,文字有時理解起來比起圖片要來的複雜,至少我是這樣以爲,我更喜歡具象化的說明同步

圖片描述

對於色調我一直有不一樣於別人的理解,原諒我看似混亂的搭配。it

綜合的總結一下,結合上圖還有以前說的,咱們能夠定出一下兩個原則(或許能夠輕鬆一點的說,而不是用「原則」)

一、線程對共享變量的全部操做都必須在本身的工做空間(內存)中進行,不能直接從主內存中讀寫
二、不一樣線程之間沒法直接訪問其餘線程中工做內存中的變量,線程間的變量值的傳遞須要經過主內存來完成

以上兩句可能須要細細體會,就像你喝咖啡後不會立馬喝下一口同樣,請回味一下。

由此咱們能夠模糊但又明確的指出共享變量可見性實現的原理

結合下圖一同說明下,線程A對共享變量的修改要想被線程B即便看到,須要通過以下兩步:

一、把工做內存A中更新過的共享變量刷新到主內存中
二、將主內存中最新的共享變量的值更新到工做內存B中

圖片描述

讓咱們從新回到主題,Java語言層面支持的可見性實現方式:
——synchronized
——volatile

而由以上的篇幅講解你也知道了實現共享變量的可見性,須要保證兩點:

一、線程修改後的共享變量值可以及時從其工做內存中刷新到主內存中
二、其餘線程可以及時把共享變量的最新值從主內存更新到本身的工做內存中

synchronized能夠實如今於它的性質:原子性(同步性)、可見性

JMM(看到這個單詞時或許你應該想到前面的中文含義)關於synchronized有這樣的一些規定:

一、線程解鎖前,必須把共享變量的最新值刷新到主內存中
二、線程加鎖時,將清空工做內存中共享變量的值,在使用共享變量時須要從主內存中從新讀取最新的值
(須要注意的是,加鎖與解鎖須要同一把鎖,這讓我想到了Redis,你想到了什麼呢?)

咱們可精華的提高下,即線程解鎖前對共享變量的修改在下次加鎖時對其餘線程是可見的

讓咱們大體看看線程執行互斥代碼(即以上的描述)的過程:

一、得到互斥鎖
二、清空工做內存
三、從主內存拷貝變量的最新副本到工做內存
四、執行代碼
五、將更改後的共享變量的值刷新到主內存
六、釋放互斥鎖

(請本身思考一次,不要把五、6步的順序顛倒了哦)

本文未完~,請期待下篇。
【Java貓說】Java多線程以內存可見性(下篇)
歡迎你留言討論屬於你的看法,畢竟每一個人的味蕾都不同,這杯咖啡有吸引到你嗎?
(好像又是一個槽糕的比喻)


本文已轉載我的技術公衆號:UncleCatMySelf
歡迎留言討論與點贊
上一篇推薦:【Java貓說】主數據類型和引用
下一篇推薦:【Java貓說】Java多線程以內存可見性(下篇)

相關文章
相關標籤/搜索