可見性:一個線程對共享變量的修改,更夠及時的被其餘線程看到安全
原子性:即不可再分了,不能分爲多步操做。好比賦值或者return。好比"a = 1;"和 "return a;"這樣的操做都具備原子性。相似"a += b"這樣的操做不具備原子性,在某些JVM中"a += b"可能要通過這樣三個步驟:多線程
1. 取出a和b
2. 計算a+b
3. 將計算結果寫入內存
複製代碼
Synchronized可以實現原子性和可見性;在Java內存模型中,synchronized規定,線程在加鎖(對象鎖,類鎖)時的工做步驟:函數
PS:優化
當synchronized修飾一個static方法時,多線程下,獲取的是類鎖(即Class自己,注意:不是實例);spa
當synchronized修飾一個非static方法時,多線程下,獲取的是對象鎖(即類的實例對象)線程
Volatile實現內存可見性是經過內存屏障store和load指令完成的;code
對volatile變量執行寫操做時,會在寫操做後加入一條store指令,即強迫線程將最新的值刷新到主內存中;對象
而在讀操做時,會加入一條load指令,即強迫從主內存中讀入變量的值。但volatile不保證volatile變量的原子性,例如:排序
Private int Num=0;
Num++;//Num不是原子操做
複製代碼
Num++不是原子操做,由於其能夠分爲:讀取Num的值,將Num的值+1,寫入最新的Num的值。內存
對於Num++;操做,線程1和線程2都執行一次,最後輸出Num的值多是:1或者2
【解釋】
輸出結果1的解釋:當線程1執行Num++;語句時,先是讀入Num的值爲0,假若此時讓出CPU執行權,線程得到執行,線程2會從新從主內存中,讀入Num的值仍是0,而後線程2執行+1操做,最後把Num=1刷新到主內存中;線程2執行完後,線程1由開始執行,但以前已經讀取的Num的值0,因此它仍是在0的基礎上執行+1操做,也就是仍是等於1,並刷新到主內存中。因此最終的結果是1.通常在多線程中使用volatile變量,爲了安全,對變量的寫入操做不能依賴當前變量的值:如Num++或者Num=Num*5這些操做。
在雙重檢查單例模式下使用 Volatile 來保證線程安全的原理就是利用了 Volatile 修飾的變量由於插入了內存屏障,禁止了指令重排,從而保證了有序性.即 User user = new User()這個操做,本來的是會被分解爲三個操做:
(a)給Singleton的實例分配內存
(b)調用Singleton()的構造函數,初始化成員字段;
(c)將singleton對象指向分配的內存空間(即singleton不爲空了);
複製代碼
變量加上volatile能夠防止對象初始化的無序性,對象必須初始化完成後纔將地址返回。