java併發程式設計實戰-第三章-物件的共享

victor123發表於2018-04-15

3.1可見性

  • 首先我們需要知道的是,java的執行緒都有自己獨立的快取,執行緒之間進行共享變數的互動是通過自身和快取和主存的互動實現的。
  • 如果執行緒的每次更改快取都刷入主存,主存每次被一個執行緒的快取修改,都通知所有的執行緒重新整理自身的快取的話,那樣就太不經濟了。
  • 由於1和2,就會產生一個現象:當執行緒1修改了一個共享變數之後,執行緒2獲取的共享變數還是更改前的值。即執行緒1更改共享變數並沒有刷入主存,或者執行緒2並沒有去主存中獲取到新的共享變數,或以上兩者皆有
  • 為了解決記憶體可見性我們可以使用volatile關鍵字和同步這兩種方式
  • 變數的讀取,指令重排序

失效資料

非原子的64位操作

當讀取一個非volatile型別的long變數時,如果對該變數的讀操作和寫操作在不同的執行緒中執行,那麼很可能讀取到某個值的高32位和另外一個值的低32位

加鎖和可見性

加鎖的含義不僅互斥,還包括記憶體可見性

volatile變數

  • 用來確保變數的更新操作通知到其他執行緒
  • 編譯器與執行時都會注意到這個變數時共享的,不會進行重排序
  • volatile不會被快取在暫存器或者其他處理器不可見的地方,讀取變數時,總能返回最新的寫入值

滿足所有條件,才應該使用volatile

  • 對變數的寫入不依賴變數的當前值,只有單個執行緒更新變臉的值
  • 該變數不會與其他狀態一起納入不變形條件中
  • 在訪問時不需要加鎖

3.2釋出與溢位

一般自定義類需要考慮釋出(構造方法), juc的類已經處理過了

釋出:是物件能夠在當前作用域之外的程式碼中使用

  • 將物件的引用儲存到公共靜態域。
  • 在非私有方法中返回引用。
  • 在釋出某物件的時候可能會間接釋出本不想釋出的物件,如一個private的陣列,一旦被髮布,其中儲存的物件也會被髮布

溢位:不應該釋出的被髮布了

類對自己的內部狀態進行了同步操作,如果釋出出去可能會破壞封裝性,並使程式難以維護不變性條件 一個物件在尚未準備好就將它釋出出去——溢位。

  • “內部類釋出也會引發溢位”,因為只有當物件通過建構函式返回之後,才處於穩定狀態。這種釋出會導致this溢位。
  • “即使在建構函式的最後一行釋出也會有該問題”,指令重排序可能會引發一些奇怪的問題。而且該引用已經不是null了,但是內容還沒有初始化完畢也有可能。
  • “不要讓this在構造期溢位!”

常見錯誤

  • 1.在建構函式中建立並啟動執行緒 這個時候執行緒已經獲得了this的引用(即使是隱式的,因為該Runnable或者Thread是所屬物件的內部類),this引用幾乎總是被新執行緒所共享。 所以在建構函式中建立執行緒沒有錯誤,但是不要在建構函式中啟動它。
  • 2.註冊一個內部類,使用this方法 可以使用靜態工廠和私有建構函式來解決這個問題。

3.3執行緒封閉

資料僅在單執行緒中被訪問,即資料不共享。 幾種方式:

特定的方法

例如在一個執行緒中進行 讀取修改寫入,其他執行緒中進行讀取

棧封閉

引用在區域性變數中 注意不要讓當前執行緒中的物件從所線上程溢位!

java的ThreadLocal

使用ThreadLocal可以做到執行緒隔離,每個執行緒都有自己單獨的一個區域儲存變數。

3.4不變性物件

  • 不可變物件滿足下列條件:
    1. 所有域是final的,域內部的域也是final的
    2. 所有域不可改變
    3. this沒有在構造的時候逸出

final域

使用volatile型別來發布不可變物件

3.5安全釋出

不正確的釋出:正確的物件被破壞

導致其他縣城看到尚未建立完的物件

不可變物件與初始化安全

安全釋出的常用模式

  1. 在靜態初始化物件引用,因為JVM的類載入過程中是同步的
  2. 對物件引用使用volatile或AtomicReference
  3. 將物件引用放入final域中
  4. 對物件引用加鎖

案例:

  • 將一個鍵值對放入HashTable,syhronizedMap或者ConcurrentMap中
  • 靜態構造的物件 public static Holder holder= new Holder(42)

事實不可變物件

程式不會去修改,例如Date雖然是可變的,但是放入了同步的HashMap中,且不會修改,那麼就認為是不可變的物件

可變物件

  • 不僅要保證釋出時的狀態可見性
  • 每次訪問時同樣需要使用同步來確保後需修改操作的可見性

小結

  • 不可變物件可以通過任意機制來發布()
  • 事實不可變物件必須通過安全方式來發布
  • 可變物件必須通過安全方式來發布,並且是執行緒安全的或者由某個所保護起來

安全的共享物件

在釋出一個物件的時候需要明確指出該物件的多執行緒共享規則:

  1. 是執行緒封閉?:只能由一個執行緒擁有
  2. 是隻讀共享?:只能併發讀
  3. 是執行緒安全共享?:類內部實現了同步,可以隨意使用
  4. 是保護物件?:類內部沒有實現同步,需要使用者在外部同步 原文地址: www.victor123.cn/2018/04/15/…

相關文章