併發程式設計實踐筆記 物件的共享之可見性

2021-09-01 17:18:18 字數 1090 閱讀 1026

當讀操作和寫操作發生在不同的執行緒中,對共享變數的讀和寫操作會產生意料之外的結果。為了確保多個執行緒之間對記憶體寫入操作的可見性,必須使用同步機制。

加鎖的含義不僅僅侷限於互斥行為,還包括記憶體可見性。為了確保所有執行緒都能看到共享變數的最新值,所有執行讀操作或者寫操作的執行緒都必須在同乙個鎖上進行。

造成記憶體可見性問題的情況有:

1.編譯器重排序。

當在沒有同步機制保護的情況下讀取某個共享變數,編譯器可能會對**的執行順序進行重排序,常見的情況有在while迴圈中判斷乙個共享的boolean變數,而在while迴圈中又沒有更改該boolean變數的值,而是在其他執行緒中修改該值,並且此時jvm的啟動模式為-server(server模式會做更多的編譯優化),就可能會發生編譯器重排序現象。

2.對於共享資料的多執行緒讀寫操作,沒有使用同乙個鎖進行保護。

常見的現象是,雖然對乙個共享物件的私有屬性進行了get和set封裝,但在get set方法中沒有使用同乙個鎖進行同步保護,在使用set方法的時候,其他執行緒依然可以使用get方法對私有屬性進行讀取而不受互斥保護。

3.非原子的64位操作。

在64位jvm中,允許將乙個64位的讀操作或者寫操作分解為兩個32位操作,所以,如果在讀取乙個非volatile型別的long或者double變數時,如果對該變數的讀操作和寫操作在不同的執行緒中執行,那麼很可能會讀到某個值的高32位和另乙個值的低32位。所以,在多執行緒程式中使用共享且可變的long或者double等型別的變數也是不安全的,除非用關鍵字volatile來宣告他們,或者用鎖保護起來。

volatile變數

當把變數宣告為volatile型別後,編譯器與執行時都會注意到這個變數是共享的,因此不會將該變數上的操作與其他記憶體操作一起重排序。volatile變數不會被快取在暫存器或者對其他處理器不可見的地方。在訪問volatile變數時不會執行加鎖操作,因此也就不會使執行執行緒阻塞,因此volatile變數是一種比synchronized關鍵字更為輕量級的同步機制。

但在使用volatile變數時需要注意,因為其在訪問時不會執行加鎖操作,所以它只適合使用在一些特別簡單的讀寫訪問場合,比如只對該變數進行一次讀或者寫操作,如果需要對可見性進行複雜的判斷(比如先判斷後改寫),那麼就不應該使用這種同步策略。

併發程式設計實踐筆記 物件的共享之發布與逸出

發布 物件的發布是指 使物件能夠在當前作用域之外的 中使用。例如在乙個非私有的方法中將私有屬性的物件引用直接返回。逸出 是指當某個不該被發布的物件被發布出去。例如,如果在物件完成構造之前就將其發布出去,就會破壞執行緒安全性。警惕this引用逸出。當從物件的建構函式中發布物件的時候,只是發布了乙個未構...

java併發程式設計實戰 物件的共享

一 可見性 當讀操作和寫操作在不同的執行緒進行的時候,並不能保證讀的執行緒可以讀到寫執行緒最新的更改。如果要確保記憶體對寫入操作的可見性,就必須使用同步。處理器還會對程式中的操作進行重排序。重排序保證在單執行緒的執 況下,和不重排序得到的結果一樣,但是多執行緒的話,就不一定了。乙個簡單的方法避免所有...

學習筆記 併發程式設計實戰 第3章 物件的共享

乙個共享變數被乙個執行緒修改後,另外乙個執行緒能夠正確的獲取到共享變數最新值,此共享變數具有可見性。缺乏同步的共享變數,在多執行緒訪問中,就可能獲取到失效資料,從而導致不可預見的錯誤。正常情況下,即使是非同步變數,不同執行緒獲取到的不是最新資料,為失效資料,但至少它是之前存在過的值,這個值曾經出現過...