Ⅰ java單例模式雙重檢驗鎖的第一次是否為空判斷是什麼目的
第一次判斷是否為空是位了保證是單例,只有初始是空的情況下才可以創建,synchronize里頭的if判空是為了避免小概率事件發生,比如當對象還沒創建時,有兩個線程都通過了外部的判空,進入synchronize入口處,此時由於同步加鎖,只有一個線程可以執行synchronize內部的代碼(生成了單例對象),當它執行完釋放了鎖後,第二個線程就進入的synchronize內部的代碼,如果此時不再判斷一下的話,該對象就再次被創建了。
Ⅱ 如何在Java中使用雙重檢查鎖實現單例
public class SingleDemo { private static SingleDemo s = null; private SingleDemo(){} public static SingleDemo getInstance(){ /*如果第一個線程獲取到了單例的實例對象, * 後面的線程再專獲取實例的時候不需要進入同步代碼屬塊中了*/ if(s == null){ //同步代碼塊用的鎖是單例的位元組碼文件對象,且只能用這個鎖 synchronized(SingleDemo.class){ if(s == null){ s = new SingleDemo(); } } } return s; } } 用這種方式解決了懶漢式的線程安全問題,也提高了效率,但是在實際開發中還是用餓漢式的比較多,畢竟這個代碼比較多,比較繁瑣。
Ⅲ 如何在java中實現singleton模式
單例模式大致有五種寫法,分別為懶漢,惡漢,靜態內部類,枚舉和雙重校驗鎖。
1、懶漢寫法,常用寫法
classLazySingleton{
;
privateLazySingleton(){
}
(){
if(singleton==null){
singleton=newLazySingleton();
}
returnsingleton;
}
}
2、惡漢寫法,缺點是沒有達到lazy loading的效果
classHungrySingleton{
=newHungrySingleton();
privateHungrySingleton(){}
(){
returnsingleton;
}
}
3、靜態內部類,優點:載入時不會初始化靜態變數INSTANCE,因為沒有主動使用,達到Lazy loading
classInternalSingleton{
{
=newInternalSingleton();
}
privateInternalSingleton(){}
(){
returnSingletonHolder.INSTANCE;
}
}
4、枚舉,優點:不僅能避免多線程同步問題,而且還能防止反序列化重新創建新的對象
enumEnumSingleton{
INSTANCE;
publicvoiddoSomeThing(){
}
}
5、雙重校驗鎖,在當前的內存模型中無效
classLockSingleton{
;
privateLockSingleton(){}
(){
if(singleton==null){
synchronized(LockSingleton.class){
if(singleton==null){
singleton=newLockSingleton();
}
}
}
returnsingleton;
}
}
Ⅳ java單例雙重檢查鎖為什麼需要加volatile關鍵字
已經修改,的確應該加上volatile關鍵字。不加的情況下,假設兩個線程,線專程A正在執行instance = new Instance()的操作,屬而線程B開始執行if(instance==null)的判斷,當不存在volatile的時候,因為 new Instance()是一個非原子操作,可能發生無序寫入,構造函數可能在整個對象構造完成前執行完畢,線程B可能會看到一個不完整的instance對象,因為java的某些實現會在內存中開辟一片存儲對象的區域後直接返回內存的引用,所以線程B判斷不為null,而這時候實際上,instance的構造函數還沒有執行,從而線程b得到不完整的對象。在 Instance 的構造函數執行之前,會在內存中開辟一片存儲對象的區域後直接返回內存的引用,賦值給變數 instance,instance也就可能成為非 null 的,即賦值語句在對象實例化之前調用,此時別的線程得到的是一個還會初始化的對象,這樣會導致系統崩潰線程B可能會看到一個不完整的instance對象,因為java的某些實現,所以線程B判斷不為null。從而得到不完整的對象。
Ⅳ 單例模式 java 雙重鎖用synchronized修飾之後還用volatile嗎
沒有volatile修飾的uniqueInstance
[java] view plain
public class Singleton {
private static Singleton uniqueInstance;
private Singleton(){
}
public static Singleton getInstance(){
if(uniqueInstance == null){ //#1
synchronized(Singleton.class){ //#2
if(uniqueInstance == null){ //#3
uniqueInstance = new Singleton(); //#4
System.out.println(Thread.currentThread().getName() + ": uniqueInstance is initalized..."); //#5.1
} else {
System.out.println(Thread.currentThread().getName() + ": uniqueInstance is not null now..."); //#5.2
}
}
}
return uniqueInstance;
}
}
這樣可能會導致結果 Singleton被實例化兩次 ,這樣就不符合單例的特點
原因分析:
1. thread2進入#1, 這時子線程的uniqueInstance都是為空的,thread2讓出CPU資源給thread3
2. thread3進入#1, 這時子線程的uniqueInstance都是為空的, thread3讓出CPO資源給thread2
3. thread2會依次執行#2,#3,#4, #5.1,最終在thread2裡面實例化了uniqueInstance。thread2執行完畢讓出CPO資源給thread3
4. thread3接著#1跑下去,跑到#3的時候,由於#1裡面拿到的uniqueInstance還是空(並沒有及時從thread2裡面拿到最新的),所以thread3仍然會執行#4,#5.1
5. 最後在thread2和thread3都實例化了uniqueInstance
例子2:用volatile修飾的uniqueInstance
這里就不貼重復的代碼了,因為只是加多一個volatile來修飾成員變數:uniqueInstance,
這樣可以創建出一個單例實例。
原因分析:
volatile(java5):可以保證多線程下的可見性;
讀volatile:每當子線程某一語句要用到volatile變數時,都會從主線程重新拷貝一份,這樣就保證子線程的會跟主線程的一致。
寫volatile: 每當子線程某一語句要寫volatile變數時,都會在讀完後同步到主線程去,這樣就保證主線程的變數及時更新。
1. thread2進入#1, 這時子線程的uniqueInstance都是為空的(java內存模型會從主線程拷貝一份uniqueInstance=null到子線程thread2),thread2讓出CPU資源給thread3
2. thread3進入#1, 這時子線程的uniqueInstance都是為空的(java內存模型會從主線程拷貝一份uniqueInstance=null到子線程thread2), thread3讓出CPO資源給thread2
3. thread2會依次執行#2,#3,#4, #5.1,最終在thread2裡面實例化了uniqueInstance(由於是volatile修飾的變數,會馬上同步到主線程的變數去)。thread2執行完畢讓出CPO資源給thread3
4. thread3接著#1跑下去,跑到#3的時候,會又一次從主線程拷貝一份uniqueInstance!=null回來,所以thread3就直接跑到了#5.2
5. 最後在thread3不再會重復實例化uniqueInstance了