单例模式的理解

简介: 谈谈你对单例模式的理解。也算是老生常谈的问题了~~~

一、作用

单个实例

保证我们的实例对象在整个应用程序中只有一个实例

二、创建方式

五种实现方式

2.1 饿汉式

在类加载的时候立即初始化,并且创建单例对象

它绝对线程安全,在线程出现以前就实例化了,不可能存在访问安全问题

优点:不加任何锁,执行效率比较高,用户体验比懒汉单例模式好

缺点:浪费内存,不管用不用都占着内存

publicclassHungrySingleton {
// 直接实例化方式//    private static final HungrySingleton hungrySingleton = new HungrySingleton();privatestaticfinalHungrySingletonhungrySingleton;
// 静态块单例模式static {
hungrySingleton=newHungrySingleton();
    }
privateHungrySingleton(){}
privatestaticHungrySingletongetInstance(){
returnhungrySingleton;
    }
}

2.2 懒汉式

在外部调用的时候才进行实例化,相比较饿汉式避免了资源浪费

在单线程情况下,比较友好。

多线程情况下,会存在线程安全问题

publicclassLazySimpleSingleton {
privateLazySimpleSingleton() {
    }
privatestaticLazySimpleSingletonlazySimpleSingleton=null;
publicstaticLazySimpleSingletongetInstance() {
if (null==lazySimpleSingleton) {
lazySimpleSingleton=newLazySimpleSingleton();
        }
returnlazySimpleSingleton;
    }
}

2.3 双重检测锁(DCL)

基于懒汉式的线程安全问题,有了双重校验锁

publicclassLazyDoubleCheckSingleton {
privatevolatilestaticLazyDoubleCheckSingletonlazyDoubleCheckSingleton=null;
/*** 私有化构造方法,防止直接通过new实例化*/privateLazyDoubleCheckSingleton() {
    }
publicstaticLazyDoubleCheckSingletongetInstance() {
if (lazyDoubleCheckSingleton==null) {
synchronized (LazyDoubleCheckSingleton.class) {
if (lazyDoubleCheckSingleton==null) {
// 1.分配内存空间 2.执行构造方法,实例化对象 3.把这个对象赋给这个空间// 不加volatile关键字,会造成指令重排,1,3,2lazyDoubleCheckSingleton=newLazyDoubleCheckSingleton();
                }
            }
        }
returnlazyDoubleCheckSingleton;
    }
}

2.4 静态内部类

publicclassStaticSingleton {
publicstaticclassInnerStaticSingleton {
/*** 声明外部类型的静态常量*/publicstaticfinalStaticSingletoninstance=newStaticSingleton();
    }
privateStaticSingleton() {
    }
publicStaticSingletongetInstance() {
returnInnerStaticSingleton.instance;
    }
}

2.5 枚举类型

publicenumEnumSingleton {
INSTANCE;
publicvoidhandleMethod(){
// 业务处理    }
}

综上的五种写法,大多都是在考虑着线程安全问题

2.6 反射爆破问题

私有的构造器,可以通过反射去破坏。

在私有构造器中进行判断,进而抛出异常。

publicstaticvoidmain(String[] args) throwsNoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
LazySimpleSingletoninstance=LazySimpleSingleton.getInstance();
Class<LazySimpleSingleton>clazz=LazySimpleSingleton.class;
Constructor<LazySimpleSingleton>constructor=clazz.getDeclaredConstructor();
constructor.setAccessible(true);
LazySimpleSingletoninstance1=constructor.newInstance();
System.out.println(instance);
System.out.println(instance1);
}
// 结果com.example.validated.design.singleton.LazySimpleSingleton@6d5380c2com.example.validated.design.singleton.LazySimpleSingleton@45ff54e6

2.7 序列化与反序列化破坏单例

LazySimpleSingleton要实现Serializable序列化接口

publicstaticvoidmain(String[] args) throwsNoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, ClassNotFoundException {
LazySimpleSingletoninstance=LazySimpleSingleton.getInstance();
Class<LazySimpleSingleton>clazz=LazySimpleSingleton.class;
Constructor<LazySimpleSingleton>constructor=clazz.getDeclaredConstructor();
constructor.setAccessible(true);
LazySimpleSingletoninstance1=constructor.newInstance();
System.out.println(instance);
System.out.println(instance1);
ObjectOutputStreamoutputStream=newObjectOutputStream(newFileOutputStream("d:/tools/a.txt"));
outputStream.writeObject(instance);
outputStream.flush();
outputStream.close();
ObjectInputStreaminputStream=newObjectInputStream(newFileInputStream("d:/tools/a.txt"));
LazySimpleSingletoninstance2= (LazySimpleSingleton) inputStream.readObject();
inputStream.close();
System.out.println(instance);
System.out.println(instance2);
}
// 结果com.example.validated.design.singleton.LazySimpleSingleton@6d5380c2com.example.validated.design.singleton.LazySimpleSingleton@45ff54e6com.example.validated.design.singleton.LazySimpleSingleton@6d5380c2com.example.validated.design.singleton.LazySimpleSingleton@5e265ba4

我们需要重写readResolve()方法

privateObjectreadResolve() {
returnlazySimpleSingleton;
}

结果:

com.example.validated.design.singleton.LazySimpleSingleton@6d5380c2com.example.validated.design.singleton.LazySimpleSingleton@45ff54e6com.example.validated.design.singleton.LazySimpleSingleton@6d5380c2com.example.validated.design.singleton.LazySimpleSingleton@6d5380c2

说明:readResolve()方法是基于回调的,反序列化时,如果定义了readResolve()则直接返回此方法指定的对象,而不需要再创建新的对象。

三、应用

在框架中看到的单例模式

  1. Spring中的Bean对象,默认是单例模式
  2. 相关的工厂对象都是单例,如:Mybatis中的SqlSessionFactory,Spring中BeanFactory
  3. 保存相关配置信息的都是单例,如:Mybatis中的Configuration对象,SpringBoot中的各个xxxAutoConfiguration对象
  4. 应用程序的日志应用,一般都会通过单例来实现
  5. 数据库的连接池的设计也是单例模式
目录
相关文章
|
2月前
|
设计模式 安全 C#
单例模式详解
单例模式是一种常用的创建型设计模式,确保某个类只有一个实例,并提供一个全局访问点。本文详细介绍了单例模式的定义、特点、适用场景、优缺点及实现代码(C++ 和 C#),并探讨了线程安全的实现细节和与依赖注入的结合使用。
49 0
|
9月前
|
设计模式
单例模式
单例模式
49 0
|
9月前
|
设计模式 安全
【单例模式】—— 每天一点小知识
【单例模式】—— 每天一点小知识
|
安全 Java
原来要这么实现单例模式
原来要这么实现单例模式
67 0
|
设计模式 C#
C# 机房重构单例模式
C# 机房重构单例模式
89 0
|
设计模式 缓存 JSON
没那么简单的单例模式
没那么简单的单例模式
138 0
没那么简单的单例模式
|
设计模式 缓存
我学会了,单例模式
单例模式属于创建型模式,这个类型的设计模式是将 对象的创建和使用解耦了,花式的去创建对象。
145 0
我学会了,单例模式
机房重构之单例模式的应用
机房重构之单例模式的应用
|
设计模式 开发框架 Java
单例模式7种实现
单例模式7种实现
160 0
|
设计模式 安全 Java
单例模式中的那些坑
本章内容涉及到java多线程,类加载机制,JVM指令重排,final以及volatile的区别,序列化问题,java反编译,反射创建对象机制等相关问题. 需要读者具备一定的Java相关基础.
5029 0
单例模式中的那些坑