單例模式應該是設計模式中最好理解,比較簡單的一種。單例模式分為兩種 懶漢式和餓漢式兩。
餓漢式是在程序編譯過程中就進行對象創建,后來的使用只是對對象調用,所以不存在線程安全問題。
懶漢式
public class Singleton {
// 創建靜態實例化對象
private static final Singleton instance = new Singleton();
// 私有化構造方法
private Singleton() {
}
// 提供靜態的對外的對象獲取方法
public static Singleton getInstance() {
return instance;
}
}
懶漢式是在使用的時候去判斷是否已經生成對象,如果是多線程的情況下,就會出現一種情況,線程1判斷完對象還是null準備實例化,線程2也進行了判斷而線程1還沒實例化完成斗導致的后果就是實例化了2次,針對這種情況,要進行加鎖。保證線程安全。
懶漢式單例加鎖
public class Singleton2 {
public static Singleton2 Singleton=null;
private Singleton2() {}
public synchronized static Singleton2 getInstance(){
if (Singleton==null) {
return Singleton=new Singleton2();
}
return Singleton;
}
}
保證了當多個進程進入第一個判斷鎖時,會被同步機制隔離,只有一個程序進入新建對象,再其他線程進入時,instance已經不為null,因此不會新建多個對象。但也有一個問題,就是java是實行無序寫入的機制,在某個線程執行的過程中,instance被賦予了地址,但是singleton對象還沒構造完成時,如果有線程訪問了此時判斷instance不為空,但是方法返回的是一個不完整對象的引用。此時可能會產生錯誤!
懶漢式單例雙重加鎖
public class Singleton3 { //適用JDK1.5及以后
private volatile static Singleton3 instance;
private Singleton3() {
}
public static Singleton3 getInstance() {
if (instance == null) {
synchronized (Singleton3.class) {
if (instance == null) {
instance = new Singleton3();//由于不是原子操作,禁止指令重排序優化,因此instance需要 volatile關鍵字修飾
}
}
}
return instance;
}
}
還有一種靜態內部類的方式,這里采用靜態初始化的方式,它由JVM來保證線程安全性,當類裝載的時候不去初始化對象,在這個內部類中去實例化對象,因此只要不使用內部類就不會創建對象,這樣能解決線程安全的問題還可以實現延遲加載且不會增加任何訪問開銷
懶漢式單例靜態內部
public class Singleton4 {
private Singleton4() {}
private static class SigletonHolder{
private static Singleton4 instance=new Singleton4();
}
public static Singleton4 getInstance(){
return SigletonHolder.instance;
}
}