单例模式的理解

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

一、作用

单个实例

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

二、创建方式

五种实现方式

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. 数据库的连接池的设计也是单例模式
目录
相关文章
|
1月前
|
设计模式 安全 Java
软件设计模式:单例模式
软件设计模式:单例模式
|
4天前
|
设计模式
单例模式
单例模式
11 0
|
3月前
|
C++
【C++ 单例模式】
【C++ 单例模式】
|
8月前
|
设计模式 安全 编译器
2023-6-12-第三式单例模式
2023-6-12-第三式单例模式
50 0
|
4月前
|
安全 C++
C++单例模式
C++单例模式
30 1
|
7月前
|
设计模式 安全 Java
单例模式的运用
单例模式的运用
25 0
|
8月前
|
安全 Java
原来要这么实现单例模式
原来要这么实现单例模式
38 0
|
存储 安全 调度
单例模式的简单介绍
单例模式的简单介绍
|
安全 Java
单例模式很简单
《基础系列》
86 0
单例模式很简单
|
设计模式 数据库 Python