單例模式 一個類只有一個實例,並提供一個全局訪問此實例的點,哪怕多線程同時訪問。 單例模式主要解決了一個全局使用的類被頻繁的創建和消費的問題。 單例模式的案例場景 資料庫的連接池不會反覆創建 spring中一個單例模式bean的生成和使用 在我們平常的代碼中需要設置全局的一些屬性保存 七種單例模式的 ...
單例模式
一個類只有一個實例,並提供一個全局訪問此實例的點,哪怕多線程同時訪問。
單例模式主要解決了一個全局使用的類被頻繁的創建和消費
的問題。
單例模式的案例場景
- 資料庫的連接池不會反覆創建
- spring中一個單例模式bean的生成和使用
- 在我們平常的代碼中需要設置全局的一些屬性保存
七種單例模式的實現
1.懶漢模式(線程不安全,懶載入)
public static class Singleton_01 {
private static Singleton_01 instance;
private Singleton_01() {}
public static Singleton_01 getInstance() {
if (null != instance) {
return instance;
}
instance = new Singleton_01();
return instance;
}
}
非常標準且簡單地單例模式。
滿足了懶載入,但是線程不安全。
2.懶漢模式(線程安全,懶載入)
public static class Singleton_02 {
private static Singleton_02 instance;
private Singleton_02() {}
public static synchronized Singleton_02 getInstance() {
if (null != instance) {
return instance;
}
instance = new Singleton_02();
return instance;
}
}
線程安全,但是效率低,不建議使用。
3.餓漢模式(線程安全)
public class Singleton_03 {
private static Singleton_03 instance = new Singleton_03();
private Singleton_03() { }
public static Singleton_03 getInstance() {
return instance;
}
}
餓漢模式,在程式啟動的時候直接運行載入。
4.使用內部靜態類(線程安全、懶載入)
public class Singleton_04 {
private static class SingletonHolder {
private static Singleton_04 instance = new Singleton_04();
}
private Singleton_04() { }
public static Singleton_04 getInstance() {
return SingletonHolder.instance;
}
}
通過內部靜態類實現了懶載入和線程安全,推薦使用。
5.鎖+雙重校驗(線程安全,懶載入)
private class Singleton_05 {
private static volatile Singleton_05 instance;
private Singleton_05() { }
public static Singleton_05 getInstance() {
if (null != instance) {
return instance;
}
synchronized (Singleton_05.class) {
if (null == instance) {
instance = new Singleton_05();
}
}
return instance;
}
}
相較於線程安全的懶漢模式,這種方法效率更高。
6.CAS【AtomicReference】(懶載入,線程安全)
public class Singleton_06 {
private static final AtomicReference<Singleton_06> INSTANCE = new AtomicReference<Singleton_06>();
private Singleton_06() { }
public static final Singleton_06 getInstance() {
for(;;) {
Singleton_06 instance = INSTANCE.get();
if (null != instance) {
return instance;
}
INSTANCE.compareAndSet(null, new Singleton_06());
return INSTANCE.get();
}
}
}
AtomicReference<V>
可以封裝引用一個V實例,支持併發訪問。
使用CAS的好處就是不需要使用傳統的加鎖方式保證線程安全,而是依賴於CAS的忙等演算法,依賴於底層硬體的實現,來保證線程安全。相對於其他鎖的實現,沒有線程的切換和阻塞,也就沒有了額外的開銷,並且可以遲遲較大的併發性。
但CAS的一個缺點就是忙等,如果一直沒有獲取到將會處於死迴圈中。
7.枚舉單例(線程安全)
public enum Singleton_07 {
INSTANCE;
private Map<String, String> cache = new ConcurrentHashMap<>();
public Map<String, String> getCache() {
return cache;
}
public void addToCache(String key, String value) {
cache.put(key, value);
}
}
《Effective Java》作者推薦使用枚舉的方式解決單例模式,這種方法解決了線程安全、自由串列化和單一實例的問題。
調用時也很方便:Singleton_07.INSTANCE.getCache();
枚舉單例十分簡潔,並且它無常地提供了串列化機制,絕對防止對此實例化(畢竟是枚舉)。
但是它在繼承的場景下是不可使用的,並且不是懶載入(畢竟是枚舉)。