日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

反骨之Java是如何解决并发中的可见性问题的

發(fā)布時間:2023/12/8 java 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 反骨之Java是如何解决并发中的可见性问题的 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

前段時間筆者寫過一篇關(guān)于, 關(guān)于《反骨之Java是如何解決并發(fā)中的原子性問題》的博文。

其中,提出一個觀點:Java中使用互斥鎖和CAS解決了并發(fā)中的原子性問題。

那么,本篇博文則主要探討的是:

Java中如何利用Java內(nèi)存模型規(guī)范中的Volatile、synchronized、final關(guān)鍵字解決可見性問題。

(想自學(xué)習(xí)編程的小伙伴請搜索圈T社區(qū),更多行業(yè)相關(guān)資訊更有行業(yè)相關(guān)免費視頻教程。完全免費哦!)

正文

在開始重點之前,我們注意到上文提到的兩個關(guān)鍵詞,即內(nèi)存模型和可見性問題。

由于筆者在《反骨之硬件&軟件為Java并發(fā)編程中挖的坑(可見性&原子性&有序性)》一文中,對可見性僅是一筆帶過,所在筆者認為在本篇博文中,需要對可見性進行更進一步的解釋。

所以,在談解決可見性問題之前,我們需要聊聊可見性問題&Java內(nèi)存模型。

什么是可見性問題呢?

  • 可見性,主要強調(diào)一個線程對某個共享變量進行更新之后,后續(xù)訪問該共享變量的線程可能無法立即讀取到更新后的最新結(jié)果,甚至永遠也無法得知其他線程對該共享變量進行過修改操作。那么,這種問題,我們可以稱其為:可見性問題。

當然,可見性問題是也是線程安全性的表現(xiàn)形式之一。

線程安全性的表現(xiàn)形式,即為:原子、有序、可見性。

什么是Java內(nèi)存模型(JMM)?

  • 簡單來說,Java內(nèi)存模型是一組規(guī)范,這些規(guī)范告訴JVM如何解決原子性、有序性和可見性問題。

我們知道Java的對象一般是放在堆內(nèi)存中的,而堆內(nèi)存是線程共享的,所以Java內(nèi)存模型的影響范圍一般只涉及堆內(nèi)存。這里,JMM有一張比較形象的圖1-1:

我們可以看到,JMM處于線程和主內(nèi)存中間,充當中介人的角色。

當線程和主內(nèi)存只能通過JMM通信的時候,那么JMM就是唯一的主宰!

接下來是JMM的自我介紹:

大家好,我叫Java memory model(JMM)
為了更好的解決并發(fā)編程中的線程安全性問題,即保證并發(fā)中的原子性、有序性和可見性。
所以,我規(guī)定了以下規(guī)則。
規(guī)則一:每個線程都有獨立的工作內(nèi)存, 即工作內(nèi)存(本地線程)
規(guī)則二:所有的共享變量都必須在主內(nèi)存中, 且只能通過JMM進行控制訪問。
規(guī)則三:所有的共享變量都必須在主內(nèi)存中,每個線程都有自己的工作內(nèi)存(本地內(nèi)存),線程的所有操作都必須在工作內(nèi)存中進行,而不能直接對主內(nèi)存進行操作。
規(guī)則四:工作內(nèi)存之間禁止互相訪問。

掌握絕對權(quán)力之后,那么JMM就可以制定可見性規(guī)范:

比如:當線程A想跟線程B通信的時候

  • 首先,線程A需要把自己本地內(nèi)存中更新后的共享變量副本,刷新到主內(nèi)存中。
  • 隨后,線程B跳過讀取本地內(nèi)存,直接向主內(nèi)存中讀取共享變量的值,將主內(nèi)存中讀取的值放入自己的本地內(nèi)存中。

從這里可以看出,JMM是通過控制主內(nèi)存和每個線程的本地內(nèi)存之間的交互,來達到可見性目的的。

眾所周知,JMM是一組抽象的復(fù)雜的規(guī)范,那么如何把抽象的、復(fù)雜的規(guī)范變成現(xiàn)實可用的方法呢?

所以,Java到底是利用什么手段或措施,保證多線程環(huán)境下,共享變量是立即可見性的(鎖&volatile&final)。

  • 手段一:synchronized或Lock——互斥鎖

在并發(fā)編程中,一旦使用互斥鎖,一般能解決所有并發(fā)問題!

所以,可見性可以使用互斥鎖進行保障。

  • 手段二:volatile關(guān)鍵字

在這里筆者并不講volitale的底層實現(xiàn)原理,具體的底層實現(xiàn)細節(jié)筆者會單獨寫一篇博文來進行介紹。

其實,被volitale關(guān)鍵字修飾的共享變量,具有兩個語義(這里的語義,可以理解為潛規(guī)則)。

  • 語義一:保證可見性。即,保證線程對共享變量的修改,對其他線程是立即可見的。

volatile關(guān)鍵字保證立即可見,其中有幾點需要注意:

其一,使用volatile關(guān)鍵字會強制將修改的值(共享變量)立即寫/讀入主內(nèi)存。

如何實現(xiàn)強制寫入主內(nèi)存?

在圖1-1中我們可以看到存在線程A和線程B。那么,當線程A對共享變量進行修改的時候,會導(dǎo)致線程B工作內(nèi)存中的共享變量副本失效。

如何實現(xiàn)強制讀取主內(nèi)存?

一旦線程工作內(nèi)存中的共享變量副本失效,那么就必須重新從主內(nèi)存中讀取最新的值。

當然,看到這里相信讀者們已經(jīng)看出來了,volitale的語義一是利用緩存一致性協(xié)議(MESI)來保證的。

  • 語義二:保證有序性。

額…好像保證有序性在本篇博文,出現(xiàn)地有點不合時宜。

反正,讀者們只需要記住:

volitale關(guān)鍵字利用禁止指令重排序和禁止編譯優(yōu)化,保證有序性。

  • 手段三:final關(guān)鍵字

有final修飾的變量(基本類型)具有不可變性,當且僅有一次賦值,一旦賦值即不可變。

不可變的變量或?qū)ο?#xff0c;我們可以稱其為線程安全變量/對象。

因為,final關(guān)鍵字修飾的變量是不可變的,在多線程環(huán)境中不管怎么操作,都是同一個值。

所以,final關(guān)鍵字是保證可見性的手段之一。

總結(jié)

  • Java內(nèi)存模型也稱為內(nèi)存一致性模型,是一些復(fù)雜規(guī)范的抽象集合,其中規(guī)定了工作內(nèi)存和主內(nèi)存的概念。

  • volitale關(guān)鍵字具有兩種語義:保證可見性&有序性。

  • final關(guān)鍵字意味著不可變(基本數(shù)據(jù)類型byte, int……),所以在多線程環(huán)境下是立即可見的。

  • synchronized、volitale、final三個關(guān)鍵字,可以看做是Java內(nèi)存模型對可見性問題提出的解決方案。

總結(jié)

以上是生活随笔為你收集整理的反骨之Java是如何解决并发中的可见性问题的的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。