DCL單例模式為什麼還需要加volatile

2021-10-07 13:24:25 字數 1287 閱讀 1645

目錄

dcl是什麼

dcl存在什麼問題

volatile如何解決dcl存在的問題

dcl:即雙重驗證加鎖

什麼是雙重驗證加鎖,看下面**

public class person 

public static person getinstance() }}

return person;

}}

不難看出,就是在單例模式下獲取例項的時候兩次驗證之間加了鎖,這樣有什麼好處呢?如果是多個執行緒同時呼叫getinstance()方法 ,執行緒a->>>到了1處,發現等於null,然後被打斷了,此時執行緒b->>>也到了1處,但是繼續執行了,加鎖,然後理所當然走完了整個流程,並new出person物件,這時輪到a了,a->>>2->>>3這時判斷不為null了,所以不會new了,直接返回已經存在的例項。假如第二次的if判斷去掉,那就又new了一次,就不算是單例了,所以這樣做就始終保證第一次被new出來的物件不會被改變。

也不能說dcl存在什麼問題,而應該是person不加volatile會有什麼問題。

new乙個物件,並不是一步完成的,而是分為多步:

1、申請記憶體空間,並且預設初始化(賦0值,半初始化)

2、呼叫構造方法,進行初始化

3、返回位址給,使person指向分配的位址空間(執行完此時person就不為null)

看起來沒什麼問題,但是要知道cpu是可能會亂序執行指令的,因為為了效率,cpu可能會將沒有必然聯絡的指令重排序,即3在2之前執行了,當然對於單執行緒這樣的操作是不會影響執行,而當有多個執行緒,而又在此時恰好有執行緒b呼叫了getinstance()方法,第乙個if,因為person指向的空間已經不為null,所以直接返回,那毫無疑問當引用person的時候,由於沒有完全初始化,就可能會產生異常或者和實際不符。那怎麼解決呢,答案就是給person新增volatile。

private volatile static person person;
volatile有兩個特性:可見性和有序性

什麼是可見性,即執行緒a修改變數x,那麼對於執行緒b應該立即可以獲取到資訊x被修改,並重新讀取

什麼是有序性,即禁止許指令重排序

禁止重排序是怎麼做到的?

答案:一是語義層次,二是記憶體屏障。記憶體屏障分為讀屏障(loadload),寫屏障(storestore),讀寫屏障(loadstore或storeload);如load,loadload,load,在進行新的讀操時,加入讀屏障,保證上一次的讀操作完成再執行新的讀操作而不會越過屏障去執行新操作。

設計模式之單例DCL為什麼需要volatile

單例模式dcl實現 pulic class singleton 類的內部宣告變數 volatile防止指令重排 private static volatile singleton singleton 對外暴露乙個靜態方法,當呼叫該方法時,才去建立例項 singleton 加入雙重檢查,解決執行緒安全...

有了互斥量,為什麼還需要條件變數?

一。互斥量和條件變數簡介 互斥量 mutex 從本質上說是一把鎖,在訪問共享資源前對互斥量進行加鎖,在訪問完成後釋放互斥量上的鎖。對互斥量進行加鎖以後,任何其他試圖再次對互斥鎖加鎖的執行緒將會阻塞直到當前執行緒釋放該互斥鎖。如果釋放互斥鎖時有多個執行緒阻塞,所有在該互斥鎖上的阻塞執行緒都會變成可執行...

通過導數求極值為什麼還需要梯度下降

一直疑惑乙個問題 對於最小二乘法,為什麼不直接求導讓導數為 0 直接求極值呢?因為實際情況有些是不可行的,比如有時候求解這樣的方程非常複雜。所以有了梯度下降 詳細請檢視 對於阿爾法的取值 檢視 這裡舉個例子 y x 2 使用通過梯度最小二乘法計算極值 x x 2x 假設隨機x 5,假設 1 第一次迭...