對Java單例模式 volatile關鍵字作用的理解
單例模式是程序設計中經常用到的,簡單便捷的設計模式,也是很多程序猿對設計模式入門的第一節課。其中最經典的一種寫法是:
class Singleton { private volatile static Singleton instance;//volatile關鍵字防止指令重排 private Singleton(){} public static Singleton getInstance() { if ( instance == null ) { //一重判斷 synchronized (Singleton.class) {//對Singleton.class上鎖 if ( instance == null ) {//二重判斷 instance = new Singleton(); } } } return instance; } }
其中有兩個關鍵的地方:1,初始化instance實例的適合,采用兩重判斷對Singleton.class上鎖。
2,靜態變量instance使用了volatile關鍵字進行修飾。
第一個問題相對比較好理解
if ( instance == null ) { //一重判斷 synchronized (Singleton.class) {//對Singleton.class上鎖 if ( instance == null ) {//二重判斷 instance = new Singleton(); } } }
sychronized關鍵字鎖住Singleton類而不是instance對象,是因為,此時instance為空,加sychronized沒有意義。
第一重判斷的意義在於:如果instance非空,就跳過了鎖的步驟,減少加鎖的資源開銷。但是由於第一重判斷在代碼鎖之外,如果不同線程同時訪問到install==null,會先後阻塞執行代碼鎖內的內容。所以在代碼鎖內加第二重判斷就很有必要了,避免第一個線程獲取實例後,第二個線程獲得資源鎖又執行了一次instance的初始化,產生兩個不同的實例。
很多人不理解instance實例在聲明時加volatile關鍵字的作用,或者是發現別人這麽寫了,自己也跟著這麽寫。對volatile關鍵字的解釋,很多地方只是提了句“當要求使用volatile 聲明的變量的值的時候,系統總是重新從它所在的內存讀取數據,它可以防止指令重排”,這裏不想對這句話進行展開解釋,想詳細了解可以參考文章
https://www.cnblogs.com/shan1393/p/8999683.html
https://www.cnblogs.com/xrq730/p/7048693.html
instance = new Singleton()看上去只有一句話,但java的初始化成員變量包括內存分配、初始化、返回對象在堆上的引用等一系列操作。對於synchronized代碼塊內,一條語句完整執行是沒有問題的。但如果發生了指令重排,先返回對象在堆上的引用,再進行初始化。對於另一個執行到一重判斷的線程來說,由於instance已經指向了堆上的一段內存空間,那麽instance就不為空,會將未完成初始化的對象返回給其他函數調用,引發一系列不安全隱患。加了volatile關鍵字,可以保證返回對象在對上的引用發生在初始化之後,避免了這種情況的發生。這就是單例模式下 instance加volatile關鍵字的作用。
對Java單例模式 volatile關鍵字作用的理解