public class Singleton{
private static Singleton sInstance = null;
private Singleton(){
//init
}
public static Singleton getInstance(){
if ( sInstance == null){
sInstance = new Singleton();
}
return sInstance;
}
}
這塊代碼在單線程中時沒有問題的,但是假設有兩個線程同時調用getInstance()方法,兩個線程進入if後發現說Instance是null,都會去實例化一個Singleton對象,這樣的話就不是單例了。下面就詳細分析一下幾種解決方案。
方案一:加載時就實例化。
private static Singleton sInstance = new Singleton();
這種方法違背了java的lazy-load原則,即使用時才加載,本例中sInstance的所在類和sInstance的類型一樣,就不存在這個問題了,但是,假若Singleton的構造方法中有比較耗時的造作時,就會大大加大了類加載的時間。
方案二:synchronized關鍵字,代碼修改如下:
public class Singleton{
private static Singleton sInstance;
private Singleton(){
//init
}
public static synchronized getInstance(){
if (sInstance == null){
sInstance = new Singleton();
}
return sInstance;
}
}
這種寫法的確能夠保證同步,但是每次在獲取實例的時候,都需要獲取同步鎖,這就比較耗時,效率不高。於是,就有了方案三和方案四。
方案三:減少獲取同步鎖的次數
public static getInstance(){
if (sInstance == null){
syncInit();
}
return sInstance;
}
private void synchronized syncInit(){
if (sInstance == null){
sInstance = new Singleton();
}
}
這種方案和方案二相比,只在單例沒有被實例化的時候纔會去競爭同步鎖,大大的降低了時間的消耗。
方案四:利用JVM在加載class的時候是同步的原理
public class Singleton{ private static class SingletonContainer{ static Singleton sInstance = new Singleton(); } private Singleton(){ //init } public static getInstance(){ return SingletonContainer.sInstance; } }
JVM在加載class的時候有一種lazy-load機制,即在需要的時候纔去加載,所以當我們第一次調用getInstance方法的時候,JVM纔會去加載SigletonContainer類,並且JVM有同步機制保證SigletonContaner類只被加載一次,這樣就保證了sIntance只被實例化一次。