1.分析清單3-1 2.syn是否禁止指令重排序 3.volatile修飾引用類型 4.java中的不變性條件指什么
要編寫正確的并發程序,關鍵問題在于:在訪問共享的可變狀態時進行正確的管理。
第二章介紹了如何通過避免多個線程在同一時刻訪問相同的數據,而本章將介紹如何共享和發布對象,從而使他們能夠安全地由多個線程同時訪問。
synchronized 1.原子性 2.內存可見性(memory visibility)3.互斥性
在沒有同步的情況下,編譯器、處理器以及運行時等都可能對操作的執行順序進行一些意想不到的調整。在缺乏足夠同步的多線程程序中,要想對內存操作的執行順序進行判斷,幾乎無法得出正確結論。
在缺乏同步的程序中,可能會出現一種產生錯誤結果的情況:失效數據。
出現失效數據的可能 :
不能保證原子性不能保證可見性存在指令重排序失效數據可能會導致一些嚴重的安全問題或者活躍性問題,比如
輸出錯誤的值使程序無法結束,無限循環意料之外的異常被破壞的數據結構不精確的計算當線程在沒有同步的情況下讀取變量時,可能會得到一個失效值,但至少這個值是由之前某個線程設置的值,而不是一個隨機值,這種安全性保證也被稱為最低安全性(out-of-thin-airsafety)
最低安全性適用于絕大多數變量,但是存在一個例外:非volatile類型的64位數值變量(double和long)。java內存模型要求,變量的讀取操作和寫入操作都必須是原子操作,但對于非volatile類型的long和double變量,jvm允許將64位的讀操作或寫操作分解為兩個32位的操作。當讀取一個非volatile類型的long變量時,如果對該變量的讀操作和寫操作在不同的線程中執行,那么很可能會讀取到某個值的高32位和另一個值的低32位。因此,即使不考慮失效數據,在多線程程序中使用共享且可變的long和double等類型變量也是不安全的,(這里是指既保證不了最低安全性,也保證不了原子性 。。。)除非
用關鍵字volatile來聲明它們用鎖保護起來內置鎖可以用于確保某個線程以一種可預測的方式來查看另一個線程的執行情況。
java語言提供了一種稍弱的同步機制,即volatile變量,用來確保將變量的更新操作通知到其他線程。當把變量聲明為volatile類型后,編譯器與運行時都會注意到這個變量是共享的,(因此不能將局部變量聲明為volatile)因此不會將該變量上的操作與其他內存操作一起重排序。volatile變量不會緩存在寄存器或者對其他處理器不可見的地方,因此在讀取volatile類型變量時總會返回最新的值。
在訪問volatile變量時不會執行加鎖操作,因此也就不會使執行線程阻塞,因此volatile變量是一種比synchronized關鍵字更輕量級的同步機制。
在當前大多數處理器架構上,讀取volatile變量的開銷只比讀取非volatile變量的開銷略高一些。
volatile變量的正確使用方式包括:
確保他們自身狀態的可見性確保他們所引用對象狀態的可見性標識一些重要的程序生命周期事件的發生(例如,初始化或關閉)volatile變量通常用作某個操作完成、發生中斷或者狀態的標志。
volatile的語義不足以確保遞增操作(count++)的原子性,除非你能確保只有一個線程對變量執行 寫 操作。
當且僅當滿足以下所有條件時,才應該使用volatile變量:
對變量的寫入操作不依賴變量的當前值,或者你能確保只有單個線程更新變量的值。該變量不會與其他狀態變量一起納入不變性條件中。在訪問變量時不需要加鎖。發布(Publish)一個對象的意思是指,使對象能夠在當前作用域之外的代碼中使用。比如:
將一個指向該對象的引用保存到其他代碼可以訪問的地方(公有靜態變量)在某一個私有的方法中返回改引用將引用傳遞到其他類的方法中(方法傳遞,即alien方法,發布一個內部類的實例)在許多情況中,我們要確保對象及其內部狀態不被發布。而在某些情況下,我們又需要發布某個對象,但如果發布時要確保線程安全性,則可能需要同步。發布內部狀態可能會破壞封裝行,并且使程序難以維持不變性條件。如果在對象構造完成之前就發布該對象,就會破壞線程安全性。
當某個不應該發布的對象被發布時,這種情況就被稱為逸出(Escape)。
當發布某個對象時,可能會間接地發布其他對象。
當發布一個對象時,在該對象的非私有域中引用的所有對象同樣會被發布。
假定有一個類C,對于C來說,“外部方法(alien)” 是指行為并不完全由C來規定的方法,包括
其他類中定義的方法類C中可以被改寫的方法(既不是私有方法也不是final方法)當把一個對象傳遞給某個外部方法時,就相當于發布了這個對象。
發布內部類實例時,也隱含地發布了外部類實例本身,因為在這個內部類的實例中包含了外部類實例的隱含引用。
不安全(正確)的對象構造過程有可能導致this逸出,比如
在構造函數中發布一個匿名內部類在構造函數中啟動一個線程在構造函數中調用一個可改寫的實例方法(alien方法)新聞熱點
疑難解答