配波尔多液的化学式:程序員:不能逃避的synchronize和volatile [復制鏈接]

2019-9-27 10:09
littleRed 閱讀:283 評論:0 贊:0
Tag:  synchronize volatile

01 原子性

恋恋波尔多 www.luaogj.com.cn 首先是我們彼此都要保持一致的觀點:原子(Atomic)操作指相應的操作是單一不可分割的操作

(1)首先是代碼例子

對int型變量conut執行counter++的操作不是原子操作

這可以分為3個操作

  • 讀取變量counter的當前值
  • 拿counter當前值和1做加法運算
  • 將counter的當前值增加1后賦值給counter變量

上面的步驟2,很有可能在執行的時候就已經被其他線程修改了,其所為的“當前值”已經是過期的

(2)或者看看百度百科的例子

我們以decl (遞減指令)為例,這是一個典型的"讀-改-寫"過程,涉及兩次內存訪問。設想在不同CPU運行的兩個進程都在遞減某個計數值,可能發生的情況是:

  • CPU A(CPU A上所運行的進程,以下同)從內存單元把當前計數值⑵裝載進它的寄存器中;
  • CPU B從內存單元把當前計數值⑵裝載進它的寄存器中。
  • CPU A在它的寄存器中將計數值遞減為1;
  • CPU B在它的寄存器中將計數值遞減為1;
  • CPU A把修改后的計數值⑴寫回內存單元。
  • CPU B把修改后的計數值⑴寫回內存單元。

內存里的計數值應該是0,然而它卻是1。兩個進程都去掉了對該共享資源的引用,但沒有一個進程能夠釋放它--兩個進程都推斷出:計數值是1,共享資源仍然在被使用

(3)我再舉例我呆想到的例子,一個姐姐和一個妹妹一起包餃子

程序員:不能逃避的synchronize和volatile

假設我們在一個黑盒環境下,就是兩姐妹都在各自小空間包餃子,然后她們把餃子通過各自的小洞口放入一個大盒子里。她們并不知道對方(比如她們兩剛剛因為媽媽不給零花錢而生氣了)

這個時候她們各自同時邊賭氣邊包了一個餃子,同時放到盒子里,媽媽跑過來問老大,盒子里有多少個了?她只知道一個。再問問老二,她也是回答一個。這個生活例子可能提交特殊,不過偶爾生活中因為信息不對稱而導致的預知結果與實際有偏差也是經常發生的

所以他們腦海就是這個情況。其實盒子里已經是2個餃子了

那么其實這個場景也像是JVM

程序員:不能逃避的synchronize和volatile

02 synchronize

synchronize關鍵字可以實現操作的原子性,其本質是通過該關鍵字所包括的臨界區的排他性保證在任何一個時刻只有一個線程能夠執行臨界區中的代碼

也就是說,現在媽媽說只有聽她的,兩姐妹才能有零花錢,所以她叫兩個鬧脾氣的小鬼都到廚房,并拿出了大盒子,讓她們重新開始,不過要按照媽媽的要求來

程序員:不能逃避的synchronize和volatile

媽媽先讓姐姐包了5個,因為兩姐妹都在廚房,不是各自在房間,所以這次妹妹都看在眼里,接著媽媽讓妹妹包10個,妹妹顯然是有點不樂意了(憑什么我姐才5個),不過她還是老實做了,現在他們三人都知道盒子里有15個

這里就又牽出了synchronize的另一個特點,保證內存的可見性

它保證了一個線程執行臨界區中的代碼時所修改的變量值對于稍有執行該臨界區中的代碼的線程來說是可見的,這對于保證多線程的代碼是非常重要的

官方的解釋下:CPU執行代碼,為了減少變量訪問的消耗,會將值緩存到CPU緩存區,再次訪問的時候,就是從緩存區去讀取而不是主內存,這里的緩存區有點類似姐姐腦海/妹妹腦海。而且代碼對緩存區的修改可能僅修改緩存區,沒有被寫回主內存。由于CPU都有自己的存儲區,對于不同CPU的存儲區內容是不可見的。這也是所謂的內存可見性

03 volatile

同樣這個兄弟也可以保證內存可見性

一個線程對于一個采用volatile修改的變量的值的更改對于其他訪問該變量的值的線程總是可見的

如果說對比synchronize和volatile的內存鎖,然后說volatile是輕量級鎖,emmmm,不好不太恰當

volatile的內部鎖并不能保證操作的原子性。

他在內存可見性的核心機制是:修改的值會被寫入主內存,且其他CPU緩存區的值會因此失效(然后再更新一個最新值),保證其他線程訪問volatile修飾的變量總是最新值。

當然他也有一個核心作用:禁止指令重排序(Re-order)

你們一般怎么寫5的?

程序員:不能逃避的synchronize和volatile

假如以上是我們的規定與希望

可能編譯器和CPU為了提供指令的執行效率可能會進行指令重排序(優化)

程序員:不能逃避的synchronize和volatile

如果你希望它是按照規定來的話就加上volatile,雖然可能會導致編譯器和CPU無法對一些指令做可能的優化,假設上面那樣寫對于計算機來說算優化:)

用程序來寫一個例子:

private SomeOne object = new SomeOne();

你先想一下,你覺得的順序,好了,我說說計算機可能的順序

  • 分配一段用于存儲SomeOne的內存空間
  • 對該內存空間引用賦值給變量object
  • 創建類SomeOne

如果當其他線程訪問object變量的時候,僅得到一個指向存儲SomeOne存儲空間的引用,因為SomeOne還沒創建

04 結語

希望各位兄弟能看到一些新的風景,synchronize可以保證操作原子性,且保證內存可見性;volatile僅能保證內存可見性。

synchronize會導致上下文切換,volatile不會哦。


我來說兩句
您需要登錄后才可以評論 登錄 | 立即注冊
facelist
所有評論(0)
領先的中文移動開發者社區
18620764416
7*24全天服務
意見反?。[email protected]

掃一掃關注我們

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 恋恋波尔多 )