Appearance

单例模式

coderzhouyu

什么是单例模式?

单例模式是一种创建型设计模式,它保证一个类只有一个实例,并提供一个全局访问点。

单例模式使用场景有哪些

一般单例模式都用在全局唯一实例的情况下
举个例子:

  1. 日志文件的写入(如果是多个资源实例可能会存在并发性问题,例如内容被覆写)
  2. 全局唯一的资源访问(例如数据库连接池,消息队列等等)

单例模式的优缺点

优点:

  1. 单例模式可以保证内存里只有一个实例,减少了内存的开销。
  2. 单例模式可以避免对资源的多重占用,例如对文件操作、对数据库的操作等等。
  3. 单例模式可以在系统设置全局的访问点,优化和共享资源的访问。

缺点:

  1. 单例模式会隐藏类之间的依赖关系,从而导致程序的不可测试
  2. 单例模式会导致代码的不可扩展,因为单例模式会将类的创建和类的使用耦合在一起,从而导致类的创建只能在类的内部进行,无法在类的外部进行。

怎么实现单例模式?

饿汉式

在类加载的时候就初始化

public class Singleton{
	private static Singleton singleton = new Singleton();
	private Singleton(){
	}
	public static Singleton getInstance(){
		return singleton;
	}
}

懒汉式

调用的时候才初始化。懒汉式存在的问题:在多线程的情况下,可能会存在多个实例的情况。
因为在多线程的情况下,可能会出现多个线程同时判断 singleton == null 的情况,从而导致多个线程同时创建实例的情况。

public class Singleton{
	private static Singleton singleton;
	private Singleton(){}
	public static Singleton getInstance(){
		if(singleton == null){
			singleton = new Singleton();
		}
		return singleton;
	}
}

双重检查

双重检查需要将 singleton 设置为 volatile 类型的,否则可能会出现指令重排的问题。
比如:线程 A 执行到 singleton = new Singleton()的时候,可能会先执行 singleton 的初始化,然后再执行 singleton = new Singleton() 的操作,这样就会导致 singleton 对象的初始化顺序发生变化,从而导致线程不安全的问题。

public class Singleton{
	private static volatile Singleton singleton;
	private Singleton(){}
	public static Singleton getInstance(){
		// 第一次检查 这个检查是为了避免不必要的同步
		if(singleton == null){
			synchronized(Singleton.class){
				// 第二次检查 这个检查是为了在 singleton == null 的情况下才创建实例
				if(singleton == null){
					singleton = new Singleton();
				}
			}
		}
		return singleton;
	}
}

静态内部类

静态内部类的方式是利用了类加载的机制,在类加载的时候,静态内部类不会被加载,只有在调用的时候才会被加载,从而实现了懒加载的效果。并且用 Java 的类加载机制来保证了线程安全。

public class Singleton{
	private Singleton(){}
	private static class SingletonHolder{
		private static final Singleton singleton = new Singleton();
	}
	public static Singleton getInstance(){
		return SingletonHolder.singleton;
	}
}

枚举

枚举的方式是利用了枚举的特性,枚举的特性是在 Java 中是天然的单例,所以可以通过枚举的方式来实现单例。

public enum Singleton{
	INSTANCE;
	public void doSomething(){
		// do something
	}
}
Last Updated 2023/10/18 09:59:31