有很多場景,都需要保證一個類僅有一個實例,爲此有一種專門的設計模式:單例模式。
單例模式的幾種寫法
1. 餓漢式(沒有延遲加載,線程安全)
public class Singleton {
private static Singleton instance = new Singleton();
public static Singleton getInstance()
{
return instance;
}
}
2. 靜態內部類(延遲加載,線程安全)
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTACE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTACE;
}
}
3. 懶漢式(有延遲加載,線程不安全)
public class Singleton {
private static Singleton instance;;
public static Singleton getInstance()
{
if(instance == null)
{
instance = new Singleton();
}
return instance;
}
}
懶漢安全式(延遲加載,線程安全,效率低)
public class Singleton {
private static Singleton instance;;
public static synchronized Singleton getInstance()
{
if(instance == null)
{
instance = new Singleton();
}
return instance;
}
}
加了關鍵字synchronized之後,能保證多線程安全,但是卻做了很多無用功,實際只有第一次初始化的時候需要同步。
加以改進,便產生了雙重校驗方式。
5.雙重校驗方式(延遲加載,線程安全,效率OK)
public class Singleton {
private volatile static Singleton instance;;
public static Singleton getInstance()
{
if(instance == null)
{
synchronized (Singleton.class) {
if(instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
爲了避免每次都同步的性能損耗,做了兩次null檢查。確保只有第一次調用單例的時候纔會做同步。
但是這在java中可能有問題,因爲同步塊外面的null檢查可能看到已經存在但不完整的實例。
因爲java平臺內存模型允許無序寫入和重排序。在構造函數執行之前,變量instance可能就已經變成非null的了。
Java 5之後可以用volatile關鍵字解決這一問題。
6. 枚舉(目前最好的方式)
public enum Singleton
{
INSTANCE;
//other methods
}
枚舉沒有延遲加載的問題,也能避免線程同步問題,還能解決單例類序列化問題,目前被認爲是最好的方式,但是感覺生疏,不易理解。
參考: