1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        淺談synchronized 和 volatitle 實(shí)現(xiàn)線(xiàn)程安全的策略

        共 2422字,需瀏覽 5分鐘

         ·

        2021-05-02 00:36

        點(diǎn)擊上方“程序員大白”,選擇“星標(biāo)”公眾號(hào)

        重磅干貨,第一時(shí)間送達(dá)


        什么是線(xiàn)程不安全

        對(duì)線(xiàn)程安全的理解就是多個(gè)線(xiàn)程同時(shí)操作一個(gè)共享變量時(shí)會(huì)產(chǎn)生意料之外的情況,這種情況就是線(xiàn)程不安全。注意:只有寫(xiě)操作才可能出現(xiàn)線(xiàn)程不安全,對(duì)共享變量只進(jìn)行讀操作線(xiàn)程是絕對(duì)安全的。

        具體線(xiàn)程不安全的例子有一個(gè)很經(jīng)典的就是兩個(gè)線(xiàn)程都對(duì)一個(gè)共享變量 x=0 執(zhí)行 100 次自增操作,但是 x 的結(jié)果并非 200

        因此線(xiàn)程不安全的條件是:多線(xiàn)程 + 共享變量 + 寫(xiě)操作


        Java的內(nèi)存模型

        你可能會(huì)好奇線(xiàn)程是如何獲取共享變量的?

        Java 線(xiàn)程之間的通信由 Java 內(nèi)存模型(簡(jiǎn)稱(chēng) JMM)控制,從抽象的角度來(lái)說(shuō),JMM 定義了線(xiàn)程和主內(nèi)存之間的抽象關(guān)系。JMM 的抽象示意圖如圖所示:

        從圖中可以看到:

        • 共享變量存在于主內(nèi)存中,也就是堆內(nèi)存

        • 每一個(gè)線(xiàn)程都保存了一份該線(xiàn)程使用到的共享變量的副本

        • 線(xiàn)程讀取共享變量?jī)?yōu)先從本地內(nèi)存(也就是棧內(nèi)存)中讀取,寫(xiě)共享變量先寫(xiě)到棧內(nèi)存,再寫(xiě)入堆內(nèi)存

        • 線(xiàn)程之間對(duì)共享變量的通信只能通過(guò)堆內(nèi)存

        以上只是 Java 內(nèi)存模型的抽象圖,實(shí)際上線(xiàn)程的工作模型是這樣的,棧內(nèi)存即是兩個(gè)緩沖區(qū)

        接下來(lái)看一個(gè)線(xiàn)程不安全的例子:

        假設(shè)線(xiàn)程 A、B 操作同一個(gè)共享變量 X,初始兩級(jí) Cache 都為空

        1. 線(xiàn)程A想要讀取X的值,由于兩級(jí) Cache 都沒(méi)有命中,因此加載堆內(nèi)存中的 X=0,并緩存到兩個(gè) Cache 中

        2. 線(xiàn)程 A 修改 X 的值為 1,為為兩個(gè) Cache 刷新 X,再刷新到堆內(nèi)存

        3. 線(xiàn)程 B 想要獲取 X 的值,一級(jí)緩存沒(méi)有獲取到,二級(jí)緩存命中,讀取到 X=1

        4. 線(xiàn)程B想要修改X的值為 2,先刷新自,己的一級(jí)緩存為 2,再刷新二級(jí)緩存和堆內(nèi)存中的 X 為 2。目前為止一切正常,接下來(lái)重點(diǎn)來(lái)了

        5. 線(xiàn)程 A 想要讀取 X 的值,一級(jí)緩存命中此時(shí) X=1,但是堆內(nèi)存中的 X=2??梢钥吹骄€(xiàn)程B寫(xiě)入的共享變量對(duì) X 不可見(jiàn),出現(xiàn)了線(xiàn)程不安全的情況。

        由于 Java 內(nèi)存機(jī)制就是這樣設(shè)計(jì)的,因此多個(gè)線(xiàn)程操作同一個(gè)變量會(huì)產(chǎn)生不安全的問(wèn)題,volatitle 關(guān)鍵字這是被設(shè)計(jì)出來(lái)解決這一問(wèn)題的,它只能用于單個(gè)變量。


        volatile解決共享變量線(xiàn)程不安全的策略

        還是接著上面這個(gè)例子,這樣定義 X

        volatle int X=0

        volatile 的內(nèi)存語(yǔ)義是:

        當(dāng)一個(gè)線(xiàn)程對(duì) volatile 修飾的變量進(jìn)行寫(xiě)操作時(shí),JMM 會(huì)立即將該線(xiàn)程對(duì)應(yīng)的棧內(nèi)存中的副本的值刷新到堆內(nèi)存中;當(dāng)一個(gè)線(xiàn)程對(duì) volatile 修飾的變量進(jìn)行讀時(shí),JMM 會(huì)清空此變量的一二級(jí)緩存,直接從堆內(nèi)存中讀取共享變量的值。

        volatile 可以當(dāng)作一個(gè)輕量級(jí)的鎖來(lái)使用,但 volatile 僅僅只能保證共享變量?jī)?nèi)存的可見(jiàn)性,不能保證操作共享變量的原子性,而鎖(如 synchronized)能保證整段鎖范圍內(nèi)的代碼具有原子性。


        synchronized 與鎖

        首先要明確的是 synchronized 不是鎖,鎖都是基于對(duì)象的( Object 的子類(lèi)),Java 中的每一個(gè)對(duì)象都可以作為一個(gè)鎖。

        synchronized 是 Java 的一個(gè)關(guān)鍵字,保證臨界區(qū)內(nèi)的代碼同一時(shí)刻只能有一個(gè)線(xiàn)程執(zhí)行。

        線(xiàn)程的執(zhí)行代碼在進(jìn)入 synchronized 代碼塊前會(huì)自動(dòng)獲取內(nèi)部鎖,這時(shí)候其他線(xiàn)程訪問(wèn)該同步代碼塊時(shí)會(huì)被阻塞掛起。拿到內(nèi)部鎖的線(xiàn)程會(huì)在正常退出同步代碼塊或者拋出異常后或者在同步塊內(nèi)調(diào)用了該內(nèi)置鎖資源的wait系列方法時(shí)釋放該內(nèi)置鎖。內(nèi)置鎖是排它鎖,也就是當(dāng)一個(gè)線(xiàn)程獲取這個(gè)鎖后,其他線(xiàn)程必須等待該線(xiàn)程釋放鎖后才能獲取該鎖。

        sysnchronized的內(nèi)部鎖可以是:

        • 當(dāng)前類(lèi)的 class 字節(jié)碼對(duì)象:this.getClass

        • 當(dāng)前類(lèi)的一個(gè)實(shí)例:this

        • 一個(gè) Object 對(duì)象

        wait 和 notify 方法只能用于 synchronized 同步代碼塊內(nèi)


        synchronized 的內(nèi)存語(yǔ)義

        與 volatile 不同

        進(jìn)入 synchronized 塊的內(nèi)存語(yǔ)義是把再 synchronized 塊內(nèi)使用到的所有共享變量從棧內(nèi)存中清空,這樣就只能從堆內(nèi)存只讀取,保證了內(nèi)存可見(jiàn)性。退出 synchronized 塊的內(nèi)存語(yǔ)義是把 synchronized 塊內(nèi)對(duì)共享變量的修改刷新到堆內(nèi)存。

        仔細(xì)想想,這其實(shí)也是一個(gè)加鎖和解鎖的過(guò)程,保證共享變量修改的可見(jiàn)性。


        總結(jié)

        • volatile 僅能保證單個(gè)共享變量?jī)?nèi)存的可見(jiàn)性,不能保證原子性。而 synchronized 既可保證同步塊內(nèi)所有共享變量的內(nèi)存可見(jiàn)性,又能保證其操作的原子性。

        • volatile 是一個(gè)輕量級(jí)的保證內(nèi)存可見(jiàn)性的關(guān)鍵字,實(shí)際上并沒(méi)有加鎖。因此它的性能很高。

        • synchronized 是一個(gè)重量級(jí)的鎖,可以用在代碼塊、普通方法以及靜態(tài)方法上。用在代碼塊時(shí)鎖就是 synchronized(~) 內(nèi)的對(duì)象,用在普通方法時(shí)鎖就是this,用在靜態(tài)方法時(shí)鎖就是 this.getClass()

        • synchronized 保證同步塊內(nèi)代碼的原子性,因?yàn)橐M(jìn)行線(xiàn)程上下文切換,性能較低。不過(guò)優(yōu)化過(guò)后性能也還可以。


        國(guó)產(chǎn)小眾瀏覽器因屏蔽視頻廣告,被索賠100萬(wàn)(后續(xù))

        年輕人“不講武德”:因看黃片上癮,把網(wǎng)站和786名女主播起訴了

        中國(guó)聯(lián)通官網(wǎng)被發(fā)現(xiàn)含木馬腳本,可向用戶(hù)推廣色情APP

        張一鳴:每個(gè)逆襲的年輕人,都具備的底層能力


        關(guān)


        ,學(xué),西學(xué)學(xué)運(yùn)營(yíng)護(hù)號(hào)樂(lè)質(zhì),結(jié)識(shí)關(guān)[],學(xué)習(xí)進(jìn)!


        瀏覽 50
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            男人j插入女人b 男女叉叉动态视频 | 性囗交免费视频观看 | 91牛| 红桃视频波多野结衣 | 不卡二区| 色阁五月 | 久久久久麻豆V国产 | 日欧 二区三区影院 | 国产精品多久久久久久情趣酒店 | 国产精品久久久久久久四虎电影 |