一、单例模式的概念
单例模式是一种创建型模式,它保证一个类只能被实例化一次,并提供了一个访问该实例的全局访问点。单例模式可以用来解决资源统一分配的问题,如线程池、全局缓存等。
单例模式的标准实现包含三个要素:1.私有的构造函数,2.静态变量实例,3.公共的访问方法。而实现单例模式的方式有很多,接下来我们将讨论这些实现方式。
二、单例模式的五种实现方式
下面是单例模式的五种实现方式:
饿汉式
public class Singleton { 一个私有的、静态的、final类型的实例对象 private static final Singleton INSTANCE = new Singleton(); 私有构造函数 private Singleton() {} 公开访问点 public static Singleton getInstance() { return INSTANCE; } }
饿汉式单例模式在类加载时已经创建了实例对象,因此不存在线程安全问题,但是如果实例对象很大,会导致启动时间变长。
懒汉式
public class Singleton { 私有的实例对象 private static Singleton instance = null; 私有构造函数 private Singleton() {} 公开访问点 public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
懒汉式单例模式在第一次调用公开访问点时才创建实例对象,解决了饿汉式单例模式启动时间长的问题。但是在多线程环境下,存在线程安全问题,需要加锁。
双重检查
public class Singleton { 一个私有的、volatile类型的实例对象 private static volatile Singleton instance = null; 私有构造函数 private Singleton() {} 公开访问点 public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
双重检查单例模式在多线程环境下解决了线程安全问题,并且不需要每次都加锁,相对于懒汉式单例模式有更优的性能。
静态内部类
public class Singleton { 私有构造函数 private Singleton() {} 私有静态内部类 private static class SingletonHolder { 一个私有的、静态的、final类型的实例对象 private static final Singleton INSTANCE = new Singleton(); } 公开访问点 public static Singleton getInstance() { return SingletonHolder.INSTANCE; } }
静态内部类单例模式在外部类被加载的时候并不会被初始化,只有在调用公开访问点时才会初始化内部类,从而创建实例对象。这种方式既避免了饿汉式单例模式的启动时间长的问题,又避免了懒汉式单例模式的线程安全问题。
枚举
public enum Singleton { INSTANCE; 公开方法 public void doSomething() {} }
枚举单例模式是饿汉式单例模式的升级版,它天生就是线程安全的,并且可以防止反序列化创建新的实例对象。其优点在于简洁明了,不需要做额外的工作即可实现单例模式。
三、单例模式的几种实现方式
除了上述五种实现方式之外,还有一些单例模式的实现方式:
延迟初始化占位类
public class Singleton { 私有变量 private String data = "this is a lazy initialized singleton"; 私有构造函数 private Singleton() {} 私有静态内部类 private static class Holder { 私有静态变量 private static final Singleton INSTANCE = new Singleton(); } 私有静态初始化方法 private static final Singleton getInstance() { return Holder.INSTANCE; } 公开访问点 public String getData() { return data; } }
延迟初始化占位模式是一种比较新颖的单例实现方式,它通过声明一个私有的静态内部类,该内部类包含单例的实例对象,并在第一次调用公开访问点时才初始化该实例对象,从而达到懒汉式单例模式的效果,并且不需要加锁保证线程安全。
ThreadLocal
public class Singleton { 一个私有的ThreadLocal类型的实例对象 private static ThreadLocalinstance = new ThreadLocal () {{ initialValue = new Singleton(); }}; 私有构造函数 private Singleton() {} 公开访问点 public static Singleton getInstance() { return instance.get(); } }
ThreadLocal单例模式是一种较为特殊的单例实现方式,它结合了懒汉式单例模式和多线程环境下线程安全的特性,并且在每个线程中都可以有自己独立的实例对象,从而达到可重入的效果。
四、单例模式的6种实现方式
除了上述介绍的五种实现方式之外,还有一种单例模式的实现方式,来看看它的实现:
注册式单例
public class Singleton { 一个私有的静态Map类型的实例对象 private static Mapregistry = new ConcurrentHashMap<>(); 静态代码块 static { Singleton instance = new Singleton(); registry.put(instance.getClass().getName(), instance); } protected Singleton() {} 公共静态方法,返回指定名称的单例对象 public static Singleton getInstance(String name) { if (name == null) { name = Singleton.class.getName(); } if (registry.get(name) == null) { registry.put(name, new Singleton()); } return registry.get(name); } }
注册式单例模式将每个实例对象都注册到一个Map容器中,以便在需要时快速获取。该实现方式相对于其他实现方式来说比较复杂,而且需要考虑线程安全问题。
五、7种方式实现单例模式
虽然单例模式的标准实现方式只有三个要素,但是在实际使用的时候,我们还可以按照以下的七种方式来实现单例模式:
枚举
和上面的枚举单例模式一样,这里就不再重复介绍了。
饿汉式
和上面的饿汉式单例模式一样,这里就不再重复介绍了。
懒汉式
和上面的懒汉式单例模式一样,这里就不再重复介绍了。
双重检查
和上面的双重检查单例模式一样,这里就不再重复介绍了。
静态内部类
和上面的静态内部类单例模式一样,这里就不再重复介绍了。
延迟初始化占位类
和上面的延迟初始化占位类单例模式一样,这里就不再重复介绍了。
ThreadLocal
和上面的ThreadLocal单例模式一样,这里就不再重复介绍了。
六、实现单例模式的类具有
实现单例模式的类具有以下特点:
1.只能被实例化一次;
2.提供一个访问该实例的全局访问点;
3.在多线程环境下保证线程安全;
4.防止反序列化时创建新的实例对象。
七、单例模式的实现
以上方式都是通过私有的构造函数、静态变量以及公开访问点来实现单例模式的。
八、单例模式代码实现
下面是一个简单的单例模式实现的示例代码:
public class Singleton { 一个私有的、静态的、final类型的实例对象 private static final Singleton INSTANCE = new Singleton(); 私有构造函数 private Singleton() {} 公开访问点 public static Singleton getInstance() { return INSTANCE; } }
九、总结
单例模式是一种重要的设计模式,它可以帮助我们解决资源统一分配的问题,并且提升了程序的可维护性和可测试性。在实际开发中,我们可以选择适合自己的单例模式实现方式,并注意线程安全问题。