单例模式
coderzhouyu
什么是单例模式?
单例模式是一种创建型设计模式,它保证一个类只有一个实例,并提供一个全局访问点。
单例模式使用场景有哪些
一般单例模式都用在全局唯一实例的情况下
举个例子:
- 日志文件的写入(如果是多个资源实例可能会存在并发性问题,例如内容被覆写)
- 全局唯一的资源访问(例如数据库连接池,消息队列等等)
单例模式的优缺点
优点:
- 单例模式可以保证内存里只有一个实例,减少了内存的开销。
- 单例模式可以避免对资源的多重占用,例如对文件操作、对数据库的操作等等。
- 单例模式可以在系统设置全局的访问点,优化和共享资源的访问。
缺点:
- 单例模式会隐藏类之间的依赖关系,从而导致程序的不可测试。
- 单例模式会导致代码的不可扩展,因为单例模式会将类的创建和类的使用耦合在一起,从而导致类的创建只能在类的内部进行,无法在类的外部进行。
怎么实现单例模式?
饿汉式
在类加载的时候就初始化
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
}
}