首先須要理解線程安全的兩個方面:執行控制和內存可見。html
執行控制的目的是控制代碼執行(順序)及是否能夠併發執行。緩存
內存可見控制的是線程執行結果在內存中對其它線程的可見性。根據Java內存模型的實現,線程在具體執行時,會先拷貝主存數據到線程本地(CPU緩存),操做完成後再把結果從線程本地刷到主存。安全
synchronized
關鍵字解決的是執行控制的問題,它會阻止其它線程獲取當前對象的監控鎖,這樣就使得當前對象中被synchronized
關鍵字保護的代碼塊沒法被其它線程訪問,也就沒法併發執行。更重要的是,synchronized
還會建立一個內存屏障,內存屏障指令保證了全部CPU操做結果都會直接刷到主存中,從而保證了操做的內存可見性,同時也使得先得到這個鎖的線程的全部操做,都happens-before於隨後得到這個鎖的線程的操做。markdown
volatile
關鍵字解決的是內存可見性的問題,會使得全部對volatile
變量的讀寫都會直接刷到主存,即保證了變量的可見性。這樣就能知足一些對變量可見性有要求而對讀取順序沒有要求的需求。併發
使用volatile
關鍵字僅能實現對原始變量(如boolen、 short 、int 、long等)操做的原子性,但須要特別注意, volatile
不能保證複合操做的原子性,即便只是i++
,實際上也是由多個原子操做組成:read i; inc; write i
,假如多個線程同時執行i++
,volatile
只能保證他們操做的i
是同一塊內存,但依然可能出現寫入髒數據的狀況。app
在Java 5提供了原子數據類型atomic wrapper classes
,對它們的increase
之類的操做都是原子操做,不須要使用sychronized
關鍵字。jvm
對於volatile關鍵字,當且僅當知足如下全部條件時可以使用:post
1. 對變量的寫入操做不依賴變量的當前值,或者你能確保只有單個線程更新變量的值。
2. 該變量沒有包含在具備其餘變量的不變式中。優化