哇喔!20种单例模式的实现与变异总结

简介: 大家好,我是 V 哥。单例模式虽然是经典问题,但依然常出现在笔试题中。如何让你的回答在众多答案中脱颖而出?本文总结了20种单例模式的实现与变异,从线程安全到资源管理,涵盖各种使用场景,帮助你在面试中脱颖而出。快来收藏吧!

大家好,我是 V 哥。再聊到单例模式,你可能会说老掉牙的问题有啥值得讲的,可能还真有,笔试题上镜率极高的一道题还在考,你的回答如何能从网络上千遍一律的回答中脱颖而出,成为卷王,是不是得来点不一样的东西呢,这20种单例模式的实现与变异总结,也许可以让你有新的发现,收藏起来吧。

单例设计模式确保一个类在整个系统中只存在一个实例,通常用于全局访问的共享资源,如数据库连接、配置文件读取、线程池等。以下V 哥总结的20种不同的实现,来看一下:

1. 饿汉式(Eager Initialization)

  • 实现:在类加载时就创建单例实例,使用finalstatic关键字定义。
  • 特点:线程安全;类加载时实例即创建,可能会导致不必要的内存占用。
public class SingletonEager {
   
    private static final SingletonEager instance = new SingletonEager();
    private SingletonEager() {
   }
    public static SingletonEager getInstance() {
   
        return instance;
    }
}

2. 懒汉式(Lazy Initialization)

  • 实现:在首次使用时才创建实例。
  • 特点:延迟加载,节省资源;不是线程安全的,适合单线程环境。
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)

  • 实现:在懒汉式基础上加入sychronized关键字,确保多线程环境的安全。
  • 特点:线程安全,但加锁会影响性能。
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)

  • 实现:在获取实例时先判断是否为空,再加锁创建实例。
  • 特点:在多线程情况下性能更高,线程安全;适合多线程环境。
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. 静态内部类(Static Inner Class)

  • 实现:利用类加载机制,延迟创建实例。
  • 特点:线程安全,懒加载,效率高。
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)

  • 实现:使用枚举类型实现单例,JVM保证了线程安全。
  • 特点:线程安全、防止反射和序列化破坏单例,是最佳实现方式之一。
public enum SingletonEnum {
   
    INSTANCE;
    public void someMethod() {
   
        // some code
    }
}

7. 使用容器实现单例(Container Singleton)

  • 实现:使用容器(如Map)存储类对象。
  • 特点:适合管理多种类型的单例;实现复杂度增加,使用场景较特殊。
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种实现方式,还有几种不同的单例模式变体,适合更复杂的使用场景:

8. 线程本地单例(ThreadLocal Singleton)

  • 实现:使用ThreadLocal变量保证每个线程有自己的单例实例。
  • 特点:每个线程会有一个单例实例,不同线程之间的实例是独立的;适合线程隔离的数据,如数据库连接或特定线程的上下文信息。
public class SingletonThreadLocal {
   
    private static final ThreadLocal<SingletonThreadLocal> threadLocalInstance =
            ThreadLocal.withInitial(SingletonThreadLocal::new);

    private SingletonThreadLocal() {
   }

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

9. CAS实现的单例(CAS-based Singleton)

  • 实现:利用java.util.concurrent.atomic.AtomicReference原子类实现无锁单例。
  • 特点:通过CAS保证线程安全,性能较高;适合高并发场景,但代码稍复杂。
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)

  • 实现:结合Enum的延迟加载特性与双重检查锁来实现。
  • 特点:利用Enum保证单例的序列化安全,结合DCL提高性能。适合对高性能和安全性要求极高的场景。
public class SingletonEnumDCL {
   
    private SingletonEnumDCL() {
   }

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

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

11. 注册表式单例(Registry Singleton)

  • 实现:通过注册表(如HashMap)集中管理多个不同类型的单例实例。
  • 特点:适合多单例实例的场景,类似于容器模式;复杂性较高,适合复杂的依赖管理系统。
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)

  • 实现:通过静态内部类加载实例。
  • 特点:一种特殊的静态内部类实现,解决了饿汉式和懒汉式的缺点;线程安全、高效、延迟加载。
public class SingletonBillPugh {
   
    private SingletonBillPugh() {
   }

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

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

13. 反射防护单例(Reflection Proof Singleton)

  • 实现:通过枚举或特殊处理反射的方式来防止反射破坏单例。
  • 特点:保护单例不被反射攻击破坏,适合安全性要求较高的场景。
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)

  • 实现:在类的finalize方法中释放资源或做额外处理。
  • 特点:确保系统资源不会被过多实例浪费,适合资源有限的情况。
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();
        // 释放资源
    }
}

除了以上列出的常见单例模式实现方式,还有一些变种实现和特殊情况的单例设计。

下面介绍一些更高级的实现方式

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

  • 实现:通过动态代理生成单例实例,控制对单例对象的访问。
  • 特点:适用于复杂的业务场景,可以在代理中加入权限控制、日志等额外逻辑。
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 {
   
            // 可以在这里加入权限控制、日志等
            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)

  • 实现:Service Locator模式是一种设计模式,通过注册中心管理单例对象,避免直接依赖实例,便于松耦合和模块化。
  • 特点:更适合用于依赖注入和模块间解耦场景,适用于复杂系统中的组件管理。
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. 对象池单例(Object Pool Singleton)

  • 实现:使用对象池模式,创建有限数量的单例对象,并在对象使用完毕后进行复用。
  • 特点:在需要复用固定数量资源时非常有效,如数据库连接池、线程池等。
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)

  • 实现:通过覆盖clone方法,防止克隆破坏单例模式。
  • 特点:保证单例对象无法通过克隆创建额外实例,防止了反射破坏单例的风险。
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. 定时刷新单例(Time-Based Singleton Refresh)

  • 实现:在指定时间或条件下重新创建单例对象,通常用于缓存或短期需要重新加载的对象。
  • 特点:适合数据或配置需要定期更新的情况,确保每个时间段获得最新的单例实例。
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)

  • 实现:使用WeakReference包装单例对象,允许Java垃圾收集器在内存不足时回收该对象。
  • 特点:适合内存敏感的场景,减少长时间未使用对象的内存占用,但对象可能会在不经意间被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;
    }
}

最后

这些变种和扩展可以用来应对不同的使用场景,从安全性到性能需求再到资源管理需求。根据特定需求,可以选择或定制合适的单例实现方式。关注威哥爱编程,编程乐无边。

相关文章
|
存储 编译器 C++
【C++】面试官:你小子,继承与多态的题你都会(下)
【C++】面试官:你小子,继承与多态的题你都会(下)
115 0
|
存储 设计模式 编译器
【C++】面试官:你小子,继承与多态的题你都会(上)
【C++】面试官:你小子,继承与多态的题你都会(上)
162 0
|
算法 Python
举一反三:三种问题,两个指针,一种方法
举一反三:三种问题,两个指针,一种方法
84 0
|
存储 安全 Java
C++第十一节——单例模式 C++11 智能指针 异常 有关讲述
可以用同样的方式来实现,就是将构造函数私有化,然后让创建类的时候只能通过一个接口函数来实现,而在这个接口函数中我们将其创建在栈上。
445 3
C++第十一节——单例模式 C++11 智能指针 异常 有关讲述
|
编译器 C++
【C++】—— 类和对象(中)一张图带你搞清楚6个默认成员函数+万字总结 复习全靠它(3)
【C++】—— 类和对象(中)一张图带你搞清楚6个默认成员函数+万字总结 复习全靠它(3)
86 0
【C++】—— 类和对象(中)一张图带你搞清楚6个默认成员函数+万字总结 复习全靠它(3)
|
人工智能 算法 定位技术
啊哈 算法读书笔记 第三章 很暴力的枚举
算法读书笔记 第三章 很暴力的枚举
80 0
|
编译器 C++
【C++】—— 类和对象(中)一张图带你搞清楚6个默认成员函数+万字总结 复习全靠它(2)
【C++】—— 类和对象(中)一张图带你搞清楚6个默认成员函数+万字总结 复习全靠它(2)
97 0
【C++】—— 类和对象(中)一张图带你搞清楚6个默认成员函数+万字总结 复习全靠它(2)
|
编译器 C++
【C++】—— 类和对象(中)一张图带你搞清楚6个默认成员函数+万字总结 复习全靠它(1)
【C++】—— 类和对象(中)一张图带你搞清楚6个默认成员函数+万字总结 复习全靠它(1)
114 0
【C++】—— 类和对象(中)一张图带你搞清楚6个默认成员函数+万字总结 复习全靠它(1)
|
算法 C++
C++ 基础复习系列2(打印图形类(循环)经典问题类)
C++ 基础复习系列2(打印图形类(循环)经典问题类)
C++ 基础复习系列2(打印图形类(循环)经典问题类)
|
编译器 C++
<C++>一篇文章搞懂类和对象中常函数和常对象的实质以及避免空指针访问的小妙招
<C++>一篇文章搞懂类和对象中常函数和常对象的实质以及避免空指针访问的小妙招
166 0
下一篇
DataWorks