反骨之Java是如何解决并发中的可见性问题的
前言
前段時間筆者寫過一篇關于, 關于《反骨之Java是如何解決并發中的原子性問題》的博文。
其中,提出一個觀點:Java中使用互斥鎖和CAS解決了并發中的原子性問題。
那么,本篇博文則主要探討的是:
Java中如何利用Java內存模型規范中的Volatile、synchronized、final關鍵字解決可見性問題。
(想自學習編程的小伙伴請搜索圈T社區,更多行業相關資訊更有行業相關免費視頻教程。完全免費哦!)
正文
在開始重點之前,我們注意到上文提到的兩個關鍵詞,即內存模型和可見性問題。
由于筆者在《反骨之硬件&軟件為Java并發編程中挖的坑(可見性&原子性&有序性)》一文中,對可見性僅是一筆帶過,所在筆者認為在本篇博文中,需要對可見性進行更進一步的解釋。
所以,在談解決可見性問題之前,我們需要聊聊可見性問題&Java內存模型。
什么是可見性問題呢?
- 可見性,主要強調一個線程對某個共享變量進行更新之后,后續訪問該共享變量的線程可能無法立即讀取到更新后的最新結果,甚至永遠也無法得知其他線程對該共享變量進行過修改操作。那么,這種問題,我們可以稱其為:可見性問題。
當然,可見性問題是也是線程安全性的表現形式之一。
線程安全性的表現形式,即為:原子、有序、可見性。
什么是Java內存模型(JMM)?
- 簡單來說,Java內存模型是一組規范,這些規范告訴JVM如何解決原子性、有序性和可見性問題。
我們知道Java的對象一般是放在堆內存中的,而堆內存是線程共享的,所以Java內存模型的影響范圍一般只涉及堆內存。這里,JMM有一張比較形象的圖1-1:
我們可以看到,JMM處于線程和主內存中間,充當中介人的角色。
當線程和主內存只能通過JMM通信的時候,那么JMM就是唯一的主宰!
接下來是JMM的自我介紹:
大家好,我叫Java memory model(JMM)
為了更好的解決并發編程中的線程安全性問題,即保證并發中的原子性、有序性和可見性。
所以,我規定了以下規則。
規則一:每個線程都有獨立的工作內存, 即工作內存(本地線程)
規則二:所有的共享變量都必須在主內存中, 且只能通過JMM進行控制訪問。
規則三:所有的共享變量都必須在主內存中,每個線程都有自己的工作內存(本地內存),線程的所有操作都必須在工作內存中進行,而不能直接對主內存進行操作。
規則四:工作內存之間禁止互相訪問。
掌握絕對權力之后,那么JMM就可以制定可見性規范:
比如:當線程A想跟線程B通信的時候
- 首先,線程A需要把自己本地內存中更新后的共享變量副本,刷新到主內存中。
- 隨后,線程B跳過讀取本地內存,直接向主內存中讀取共享變量的值,將主內存中讀取的值放入自己的本地內存中。
從這里可以看出,JMM是通過控制主內存和每個線程的本地內存之間的交互,來達到可見性目的的。
眾所周知,JMM是一組抽象的復雜的規范,那么如何把抽象的、復雜的規范變成現實可用的方法呢?
所以,Java到底是利用什么手段或措施,保證多線程環境下,共享變量是立即可見性的(鎖&volatile&final)。
- 手段一:synchronized或Lock——互斥鎖
在并發編程中,一旦使用互斥鎖,一般能解決所有并發問題!
所以,可見性可以使用互斥鎖進行保障。
- 手段二:volatile關鍵字
在這里筆者并不講volitale的底層實現原理,具體的底層實現細節筆者會單獨寫一篇博文來進行介紹。
其實,被volitale關鍵字修飾的共享變量,具有兩個語義(這里的語義,可以理解為潛規則)。
- 語義一:保證可見性。即,保證線程對共享變量的修改,對其他線程是立即可見的。
volatile關鍵字保證立即可見,其中有幾點需要注意:
其一,使用volatile關鍵字會強制將修改的值(共享變量)立即寫/讀入主內存。
如何實現強制寫入主內存?
在圖1-1中我們可以看到存在線程A和線程B。那么,當線程A對共享變量進行修改的時候,會導致線程B工作內存中的共享變量副本失效。
如何實現強制讀取主內存?
一旦線程工作內存中的共享變量副本失效,那么就必須重新從主內存中讀取最新的值。
當然,看到這里相信讀者們已經看出來了,volitale的語義一是利用緩存一致性協議(MESI)來保證的。
- 語義二:保證有序性。
額…好像保證有序性在本篇博文,出現地有點不合時宜。
反正,讀者們只需要記住:
volitale關鍵字利用禁止指令重排序和禁止編譯優化,保證有序性。
- 手段三:final關鍵字
有final修飾的變量(基本類型)具有不可變性,當且僅有一次賦值,一旦賦值即不可變。
不可變的變量或對象,我們可以稱其為線程安全變量/對象。
因為,final關鍵字修飾的變量是不可變的,在多線程環境中不管怎么操作,都是同一個值。
所以,final關鍵字是保證可見性的手段之一。
總結
-
Java內存模型也稱為內存一致性模型,是一些復雜規范的抽象集合,其中規定了工作內存和主內存的概念。
-
volitale關鍵字具有兩種語義:保證可見性&有序性。
-
final關鍵字意味著不可變(基本數據類型byte, int……),所以在多線程環境下是立即可見的。
-
synchronized、volitale、final三個關鍵字,可以看做是Java內存模型對可見性問題提出的解決方案。
總結
以上是生活随笔為你收集整理的反骨之Java是如何解决并发中的可见性问题的的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 程序员不得不学的操作系统知识(三)
- 下一篇: Nginx sendfile作用