20種單例模式實(shí)現(xiàn)與變異總結(jié) - 單例模式全面解析

2024-12-31 09:41 更新

大家好,我是 V 哥。再聊到單例模式,你可能會(huì)說老掉牙的問題有啥值得講的,可能還真有,筆試題上鏡率極高的一道題還在考,你的回答如何能從網(wǎng)絡(luò)上千遍一律的回答中脫穎而出,成為卷王,是不是得來點(diǎn)不一樣的東西呢,這20種單例模式的實(shí)現(xiàn)與變異總結(jié),也許可以讓你有新的發(fā)現(xiàn),收藏起來吧。

單例設(shè)計(jì)模式確保一個(gè)類在整個(gè)系統(tǒng)中只存在一個(gè)實(shí)例,通常用于全局訪問的共享資源,如數(shù)據(jù)庫連接、配置文件讀取、線程池等。以下V 哥總結(jié)的20種不同的實(shí)現(xiàn),來看一下:

1. 餓漢式(Eager Initialization)

  • 實(shí)現(xiàn):在類加載時(shí)就創(chuàng)建單例實(shí)例,使用finalstatic關(guān)鍵字定義。
  • 特點(diǎn):線程安全;類加載時(shí)實(shí)例即創(chuàng)建,可能會(huì)導(dǎo)致不必要的內(nèi)存占用。

public class SingletonEager {
    private static final SingletonEager instance = new SingletonEager();
    private SingletonEager() {}
    public static SingletonEager getInstance() {
        return instance;
    }
}

2. 懶漢式(Lazy Initialization)

  • 實(shí)現(xiàn):在首次使用時(shí)才創(chuàng)建實(shí)例。
  • 特點(diǎn):延遲加載,節(jié)省資源;不是線程安全的,適合單線程環(huán)境。

public class SingletonLazy {
    private static SingletonLazy instance;
    private SingletonLazy() {}
    public static SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }
}

3. 線程安全的懶漢式(Synchronized Lazy Initialization)

  • 實(shí)現(xiàn):在懶漢式基礎(chǔ)上加入sychronized關(guān)鍵字,確保多線程環(huán)境的安全。
  • 特點(diǎn):線程安全,但加鎖會(huì)影響性能。

public class SingletonLazySync {
    private static SingletonLazySync instance;
    private SingletonLazySync() {}
    public static synchronized SingletonLazySync getInstance() {
        if (instance == null) {
            instance = new SingletonLazySync();
        }
        return instance;
    }
}

4. 雙重檢查鎖(Double-Checked Locking)

  • 實(shí)現(xiàn):在獲取實(shí)例時(shí)先判斷是否為空,再加鎖創(chuàng)建實(shí)例。
  • 特點(diǎn):在多線程情況下性能更高,線程安全;適合多線程環(huán)境。

public class SingletonDCL {
    private static volatile SingletonDCL instance;
    private SingletonDCL() {}
    public static SingletonDCL getInstance() {
        if (instance == null) {
            synchronized (SingletonDCL.class) {
                if (instance == null) {
                    instance = new SingletonDCL();
                }
            }
        }
        return instance;
    }
}

5. 靜態(tài)內(nèi)部類(Static Inner Class)

  • 實(shí)現(xiàn):利用類加載機(jī)制,延遲創(chuàng)建實(shí)例。
  • 特點(diǎn):線程安全,懶加載,效率高。

public class SingletonInnerClass {
    private SingletonInnerClass() {}
    private static class Holder {
        private static final SingletonInnerClass INSTANCE = new SingletonInnerClass();
    }
    public static SingletonInnerClass getInstance() {
        return Holder.INSTANCE;
    }
}

6. 枚舉單例(Enum Singleton)

  • 實(shí)現(xiàn):使用枚舉類型實(shí)現(xiàn)單例,JVM保證了線程安全。
  • 特點(diǎn):線程安全、防止反射和序列化破壞單例,是最佳實(shí)現(xiàn)方式之一。

public enum SingletonEnum {
    INSTANCE;
    public void someMethod() {
        // some code
    }
}

7. 使用容器實(shí)現(xiàn)單例(Container Singleton)

  • 實(shí)現(xiàn):使用容器(如Map)存儲(chǔ)類對(duì)象。
  • 特點(diǎn):適合管理多種類型的單例;實(shí)現(xiàn)復(fù)雜度增加,使用場景較特殊。

import java.util.HashMap;
import java.util.Map;


public class SingletonContainer {
    private static Map<String, Object> instanceMap = new HashMap<>();
    private SingletonContainer() {}
    public static void registerInstance(String key, Object instance) {
        if (!instanceMap.containsKey(key)) {
            instanceMap.put(key, instance);
        }
    }
    public static Object getInstance(String key) {
        return instanceMap.get(key);
    }
}

除了常見的7種實(shí)現(xiàn)方式,還有幾種不同的單例模式變體,適合更復(fù)雜的使用場景:

8. 線程本地單例(ThreadLocal Singleton)

  • 實(shí)現(xiàn):使用ThreadLocal變量保證每個(gè)線程有自己的單例實(shí)例。
  • 特點(diǎn):每個(gè)線程會(huì)有一個(gè)單例實(shí)例,不同線程之間的實(shí)例是獨(dú)立的;適合線程隔離的數(shù)據(jù),如數(shù)據(jù)庫連接或特定線程的上下文信息。

public class SingletonThreadLocal {
    private static final ThreadLocal<SingletonThreadLocal> threadLocalInstance =
            ThreadLocal.withInitial(SingletonThreadLocal::new);


    private SingletonThreadLocal() {}


    public static SingletonThreadLocal getInstance() {
        return threadLocalInstance.get();
    }
}

9. CAS實(shí)現(xiàn)的單例(CAS-based Singleton)

  • 實(shí)現(xiàn):利用java.util.concurrent.atomic.AtomicReference原子類實(shí)現(xiàn)無鎖單例。
  • 特點(diǎn):通過CAS保證線程安全,性能較高;適合高并發(fā)場景,但代碼稍復(fù)雜。

import java.util.concurrent.atomic.AtomicReference;


public class SingletonCAS {
    private static final AtomicReference<SingletonCAS> INSTANCE = new AtomicReference<>();


    private SingletonCAS() {}


    public static SingletonCAS getInstance() {
        while (true) {
            SingletonCAS current = INSTANCE.get();
            if (current != null) {
                return current;
            }
            current = new SingletonCAS();
            if (INSTANCE.compareAndSet(null, current)) {
                return current;
            }
        }
    }
}

10. 枚舉雙重鎖單例(Enum Holder with DCL)

  • 實(shí)現(xiàn):結(jié)合Enum的延遲加載特性與雙重檢查鎖來實(shí)現(xiàn)。
  • 特點(diǎn):利用Enum保證單例的序列化安全,結(jié)合DCL提高性能。適合對(duì)高性能和安全性要求極高的場景。

public class SingletonEnumDCL {
    private SingletonEnumDCL() {}


    private enum Holder {
        INSTANCE;
        private final SingletonEnumDCL instance = new SingletonEnumDCL();
    }


    public static SingletonEnumDCL getInstance() {
        return Holder.INSTANCE.instance;
    }
}

11. 注冊(cè)表式單例(Registry Singleton)

  • 實(shí)現(xiàn):通過注冊(cè)表(如HashMap)集中管理多個(gè)不同類型的單例實(shí)例。
  • 特點(diǎn):適合多單例實(shí)例的場景,類似于容器模式;復(fù)雜性較高,適合復(fù)雜的依賴管理系統(tǒng)。

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


public class SingletonRegistry {
    private static final Map<String, Object> registry = new ConcurrentHashMap<>();


    private SingletonRegistry() {}


    public static void registerSingleton(String key, Object instance) {
        registry.putIfAbsent(key, instance);
    }


    public static Object getSingleton(String key) {
        return registry.get(key);
    }
}

12. Bill Pugh單例(Bill Pugh Singleton)

  • 實(shí)現(xiàn):通過靜態(tài)內(nèi)部類加載實(shí)例。
  • 特點(diǎn):一種特殊的靜態(tài)內(nèi)部類實(shí)現(xiàn),解決了餓漢式和懶漢式的缺點(diǎn);線程安全、高效、延遲加載。

public class SingletonBillPugh {
    private SingletonBillPugh() {}


    private static class SingletonHelper {
        private static final SingletonBillPugh INSTANCE = new SingletonBillPugh();
    }


    public static SingletonBillPugh getInstance() {
        return SingletonHelper.INSTANCE;
    }
}

13. 反射防護(hù)單例(Reflection Proof Singleton)

  • 實(shí)現(xiàn):通過枚舉或特殊處理反射的方式來防止反射破壞單例。
  • 特點(diǎn):保護(hù)單例不被反射攻擊破壞,適合安全性要求較高的場景。

public class SingletonReflectionProof {
    private static final SingletonReflectionProof INSTANCE = new SingletonReflectionProof();


    private SingletonReflectionProof() {
        if (INSTANCE != null) {
            throw new IllegalStateException("Instance already created!");
        }
    }


    public static SingletonReflectionProof getInstance() {
        return INSTANCE;
    }
}

14. 資源管理單例(Resource Management Singleton)

  • 實(shí)現(xiàn):在類的finalize方法中釋放資源或做額外處理。
  • 特點(diǎn):確保系統(tǒng)資源不會(huì)被過多實(shí)例浪費(fèi),適合資源有限的情況。

public class SingletonResource {
    private static final SingletonResource INSTANCE = new SingletonResource();

    
    private SingletonResource() {
        // 初始化資源
    }


    public static SingletonResource getInstance() {
        return INSTANCE;
    }


    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        // 釋放資源
    }
}

除了以上列出的常見單例模式實(shí)現(xiàn)方式,還有一些變種實(shí)現(xiàn)和特殊情況的單例設(shè)計(jì)。

下面介紹一些更高級(jí)的實(shí)現(xiàn)方式

15. 接口代理單例(Interface Proxy Singleton)

  • 實(shí)現(xiàn):通過動(dòng)態(tài)代理生成單例實(shí)例,控制對(duì)單例對(duì)象的訪問。
  • 特點(diǎn):適用于復(fù)雜的業(yè)務(wù)場景,可以在代理中加入權(quán)限控制、日志等額外邏輯。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class SingletonProxy {
    private static final MySingletonInterface INSTANCE = 
        (MySingletonInterface) Proxy.newProxyInstance(
            MySingletonInterface.class.getClassLoader(),
            new Class[]{MySingletonInterface.class},
            new SingletonHandler(new MySingleton())
        );


    private SingletonProxy() {}


    public static MySingletonInterface getInstance() {
        return INSTANCE;
    }


    private static class SingletonHandler implements InvocationHandler {
        private final MySingleton instance;


        public SingletonHandler(MySingleton instance) {
            this.instance = instance;
        }


        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 可以在這里加入權(quán)限控制、日志等
            return method.invoke(instance, args);
        }
    }
}


interface MySingletonInterface {
    void doSomething();
}


class MySingleton implements MySingletonInterface {
    @Override
    public void doSomething() {
        System.out.println("Doing something...");
    }
}

16. Service Locator單例(Service Locator Singleton)

  • 實(shí)現(xiàn):Service Locator模式是一種設(shè)計(jì)模式,通過注冊(cè)中心管理單例對(duì)象,避免直接依賴實(shí)例,便于松耦合和模塊化。
  • 特點(diǎn):更適合用于依賴注入和模塊間解耦場景,適用于復(fù)雜系統(tǒng)中的組件管理。

import java.util.HashMap;
import java.util.Map;


public class ServiceLocator {
    private static final Map<Class<?>, Object> services = new HashMap<>();


    private ServiceLocator() {}


    public static <T> void registerService(Class<T> serviceClass, T instance) {
        services.put(serviceClass, instance);
    }


    @SuppressWarnings("unchecked")
    public static <T> T getService(Class<T> serviceClass) {
        return (T) services.get(serviceClass);
    }
}

17. 對(duì)象池單例(Object Pool Singleton)

  • 實(shí)現(xiàn):使用對(duì)象池模式,創(chuàng)建有限數(shù)量的單例對(duì)象,并在對(duì)象使用完畢后進(jìn)行復(fù)用。
  • 特點(diǎn):在需要復(fù)用固定數(shù)量資源時(shí)非常有效,如數(shù)據(jù)庫連接池、線程池等。

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;


public class SingletonObjectPool {
    private static final int POOL_SIZE = 5;
    private static final Queue<SingletonObjectPool> pool = new ConcurrentLinkedQueue<>();


    static {
        for (int i = 0; i < POOL_SIZE; i++) {
            pool.add(new SingletonObjectPool());
        }
    }


    private SingletonObjectPool() {}


    public static SingletonObjectPool getInstance() {
        SingletonObjectPool instance = pool.poll();
        if (instance == null) {
            instance = new SingletonObjectPool();
        }
        return instance;
    }


    public void release() {
        pool.offer(this);
    }
}

18. 克隆防御單例(Clone-Proof Singleton)

  • 實(shí)現(xiàn):通過覆蓋clone方法,防止克隆破壞單例模式。
  • 特點(diǎn):保證單例對(duì)象無法通過克隆創(chuàng)建額外實(shí)例,防止了反射破壞單例的風(fēng)險(xiǎn)。

public class SingletonCloneProof implements Cloneable {
    private static final SingletonCloneProof INSTANCE = new SingletonCloneProof();


    private SingletonCloneProof() {}


    public static SingletonCloneProof getInstance() {
        return INSTANCE;
    }


    @Override
    protected Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException("Cannot clone singleton instance");
    }
}

19. 定時(shí)刷新單例(Time-Based Singleton Refresh)

  • 實(shí)現(xiàn):在指定時(shí)間或條件下重新創(chuàng)建單例對(duì)象,通常用于緩存或短期需要重新加載的對(duì)象。
  • 特點(diǎn):適合數(shù)據(jù)或配置需要定期更新的情況,確保每個(gè)時(shí)間段獲得最新的單例實(shí)例。

public class SingletonTimeBased {
    private static SingletonTimeBased instance;
    private static long lastCreatedTime = System.currentTimeMillis();
    private static final long REFRESH_INTERVAL = 30000; // 30 seconds


    private SingletonTimeBased() {}


    public static synchronized SingletonTimeBased getInstance() {
        if (instance == null || System.currentTimeMillis() - lastCreatedTime > REFRESH_INTERVAL) {
            instance = new SingletonTimeBased();
            lastCreatedTime = System.currentTimeMillis();
        }
        return instance;
    }
}

20. 弱引用單例(Weak Reference Singleton)

  • 實(shí)現(xiàn):使用WeakReference包裝單例對(duì)象,允許Java垃圾收集器在內(nèi)存不足時(shí)回收該對(duì)象。
  • 特點(diǎn):適合內(nèi)存敏感的場景,減少長時(shí)間未使用對(duì)象的內(nèi)存占用,但對(duì)象可能會(huì)在不經(jīng)意間被GC回收。

import java.lang.ref.WeakReference;


public class SingletonWeakReference {
    private static WeakReference<SingletonWeakReference> instanceRef;


    private SingletonWeakReference() {}


    public static synchronized SingletonWeakReference getInstance() {
        SingletonWeakReference instance = (instanceRef == null) ? null : instanceRef.get();
        if (instance == null) {
            instance = new SingletonWeakReference();
            instanceRef = new WeakReference<>(instance);
        }
        return instance;
    }
}

最后

這些變種和擴(kuò)展可以用來應(yīng)對(duì)不同的使用場景,從安全性到性能需求再到資源管理需求。根據(jù)特定需求,可以選擇或定制合適的單例實(shí)現(xiàn)方式。關(guān)注威哥愛編程,編程樂無邊。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)