可見性
併發程序對變量的寫入造成變量狀態的改變後,其它線程能夠即時的讀到完整的、最新的值。
volatile
加鎖機制既能確保可見性也能確保原子性,volatile只能確保可見性;
volatile變量的使用條件
- 對變量的寫入操作不依賴變量的當前值,或者你能確保只有單個線程更新變量的值;
- 該變量不會與其它狀態變量一起納入不變性條件中;
- 在訪問變量時不需要加鎖;
發佈與逸出章節中,逸出的幾種可能
- 如果從非私有方法中返回一個引用,就會發布返回的對象
- 當發佈一個對象時,在該對象的非私有域中引用的所有對象同樣會被髮布,此發佈關係存在傳遞關係
- 最後一種發佈對象或其內部狀態的機制就是發佈一個內容的類實例;
如果this引用在構造過程中逸出,那麼這種對象就被認爲是不正確構造
幾種對象構造過程中的逸出情況以及處理方法
- 在構造函數中啓動一個線程,this會逸出到新的線程中,如果新線程直接啓動,就可能訪問到未就緒的對象。這個時候,可以考慮在構造函數之後,再通過start或者initialize方法來延遲啓動線程;
- 如果想在構造函數中註冊一個事件監聽器或者啓動線程,那麼可以使用一個私有的構造函數和一個公共的工廠方法,從而避免不正確的構造過程。
線程封閉的幾種可靠類型
- 棧封閉 局部方法中,只要確保變量不會逸出,就可以實現線程封閉,是線程安全的。
- ThreadLocal 每一個線程都會來存有一個變量的副本,這個副本是通過實現ThreadLocal的initValue方法來初使化的,但要注意,如果我們使用set方法設置了變量,就需要特別注意,不能讓這個對象在多個線程之間被共享,否則仍然會突破線程封閉的限制。
不可變對象是線程安全的,那如何定義不可變對象呢?
- 對象創建以後其狀態就不能修改
- 對象的所有域都是final域
- 對象是正確創建的(對象的創建過程中,this引用沒有逸出)
縮小域範圍的勸告
正如“除非需要更高的可見性,否則應將所有的域都聲明爲私有域”,是一個良好的編程習慣,“除非需要某個域是可變的,否則就將所有的域都聲明爲final域”,也是一個良好的編程習慣。
安全發佈的方式
- 靜態初始化方式法中初始化一個對象引用
- 將對象的引用保存在volatile類型的域或者atomicReferance對象中
- 將對象的引用保存到某個正確構造對象的final域中
- 將對象的引用保存到一個由鎖保護的域中
在沒有額外的同步的情況下,任何線程都可以安全地使用被安全發佈的事實不可變對象
對象的發佈需求取決於它的可見性
- 不可變對象可以以任何方式發佈
- 事實不可變對象必須以安全方式發佈
- 可變對象必須以安全方式發佈,並且必須是線程安全或者某個由鎖保護起來
併發程序中使用與共享對象時的一些實用策略
- 線程封閉。只能由一個線程擁有,因此不需要考慮同步的問題。
- 只讀共享。包括不可變對象以及事實不可變對象。
- 線程安全共享。在類內部實現同步,因此多個線程可以通過對象的公有接口進行訪問而不需要進一步的同步
- 保護對象。只能通過持有某個鎖來訪問對象,包括鎖某個區域以及封裝在一些線程安全的類裏邊等;