Java中單例模式是一種經常用到的模式,並且在面試中是經常出現的,那麼我們今天就看看各種實現方式。
- 單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。這種模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。
- 餓漢模式
public class Singleton{
private static Singleton singleton = new Singleton();
private Singleton(){};
public static Singleton() getInstance(){
return singleton;
}
}
- 優點 : 類加載的時候就創建一次,並且是線程安全的。
- 缺點 : 即使這個類沒有被用到也會被創建,所以內存會被浪費。
- 懶漢模式
public class Singleton{
private static Singleton singleton = null;
private Singleton (){};
public static Singleton getInstance(){
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
- 優點: 在需要的時候纔會被創建。
- 缺點: 線程不安全。於是我們加上線程鎖
public class Singleton{
private static Singleton singleton = null;
private Singleton (){};
public static synchronized Singleton getInstance(){
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
- 優點: 解決了線程安全問題。
- 缺點: 線程鎖寫在方法上這樣性能會很差,因爲在每次調用方法的時候都會進行鎖操作。
- 雙重校驗鎖(推薦)
public class Singleton{
private static Singleton singleton = null ;
private Singleton (){};
public statice Singleton getInstance(){
if(singleton == null) { // 第一次檢查
synchronized (Singleton.class) {
if(singleton == null) { // 第二次檢查
singleton = new Singleton();
}
}
}
return singleton;
}
}
- 優點: 線程安全,並且是延時加載,有兩次判斷會提高執行效率。
- 缺點: 存在雙檢鎖隱患。
- 什麼是雙檢鎖隱患?
- 簡單了的說由於java虛擬機裏存在指令重排,所謂指令重排優化是隻在不改變元語句含義的同時調整指令的 執行順序讓程序執行的更快。就是由於指令重排的存在就會導致初始化Singleton將對象的內存地址賦值給singleton字段的順序是不確定的。
- 但是在jdk1.5之後java推出了volatile 關鍵字(如果對volatile關鍵字感興趣可以出門google一下),此關鍵字會解決指令重排。於是會解決雙檢鎖隱患。
public class Singleton{
// 使用volatile 關鍵字防止指令重排
private static volatile Singleton singleton = null ;
private Singleton (){};
public statice Singleton getInstance(){
if(singleton == null) { // 第一次檢查
synchronized (Singleton.class) {
if(singleton == null) { // 第二次檢查
singleton = new Singleton();
}
}
}
return singleton;
}
}
- 靜態內部類(推薦)
public class Singleton{
private static class SingletonHolder {
public static Singleton singleton = new Singleton();
}
private Singleton(){};
public static Singleton getInstance(){
teturn SingletonHolder.singleton;
}
}
- 優點: 同樣使用了類加載器的方式創建了對象,與餓漢式一樣,同時保證了延時加載和線程安全。