type
status
date
slug
summary
tags
category
password
1、单例模式是什么
单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。
单例模式的适用场景:
- 需要频繁创建和获取的对象,例如 Spring 容器的各种 Bean。
- 创建对象耗时或资源消耗大,例如 Servlet 的
ServletContext
。
- 工具类对象,例如 Jackson 的
ObjectMapper
。
- 频繁访问数据库或文件的对象,例如 Druid 的
DruidDataSource
。
- 需要控制共享资源访问的场景,例如 Spring 容器的
ApplicationContext
。
2、单例模式的实现
2.1 懒汉式(非线程安全)
这种是最简单的单例写法,特点:
- 构造方法使用
private
关键字,确保外部只能通过getInstance()
获取单例。
- 实例对象使用
static
关键字,确保全局只有一个实例。
- 线程不安全:并发时可能出现多个单例。
其中最大的问题就是线程不安全,后面的多种方案都是为了解决线程不安全的问题。
2.2 饿汉式(线程安全)
相比起非线程安全的懒汉式写法,最大区别就是在类初始化的时候提前创建好实例。
特点:
- 线程安全:因为提前创建了,所以是天生的线程安全。
- 性能影响:在类加载的时候就会初始化,这对应用的启动会造成一定程度的影响。
2.3 懒汉式(使用synchronized)
相比起非线程安全的懒汉式写法,最大区别就是
getInstance()
方法使用 synchronized
修饰。特点:
- 线程安全:因为使用了
synchronized
修饰getIntance()
方法,可以保证获取实例一定是线程安全。
- 性能较差:每次获取实例之前都需要先获取锁,导致性能较差。
2.4 懒汉式(Double Check)
单例模式最经典的双重检查(Double Check)写法,相比起使用
synchronized
修饰 getInstance()
的懒汉模式,最大区别就是- 使用
volatile
修饰实例(为什么要用volatile
后面会解释)。
synchronized
不再用来修饰getInstance()
方法,而是用来修饰Singleton
类,并增加一段双重判断的逻辑。
特点:
- 线程安全。
- 性能较高:只在第一次创建实例的时候需要获取锁,后续在获取实例的时候可以直接返回,不需要获取锁。
2.5 饿汉式(静态内部类,推荐)
这种方式巧妙地解决了饿汉式(线程安全)的内存浪费问题和 synchronized 的性能问题。推荐使用这种方式。
- 避免内存浪费:内部类在方法被调用之前才会初始化,所以不会造成内存浪费和影响程序启动速度。
- 线程安全:内部类里面的实例其实就是饿汉式(线程安全)的写法,所以可以保证线程安全。
2.6 枚举实现(官方推荐)
《Effective Java》推荐枚举的方式,特点:
- 线程安全:枚举类型默认就是安全的
- 避免反序列化破坏单例
但是枚举类型会造成更多的内存消耗。枚举会比使用静态变量多消耗两倍的内存,如果是 Android 应用,尽量避免这种方式。
3、单例模式常见问题
3.1 为什么单例模式中的Double Check要加volatile
volatile
的作用是禁止指令重排序。在 java 里面 new 一个对象在 JVM 层面并不是一个原子操作,大概分为以下三条指令:- 申请一块内存,此时成员变量赋默认值。
- 调用类的构造方法,给成员变量赋初始值。
- 将这块内存区域赋值给对应的变量。
JIT 编译器在进行代码优化的时候,有可能会进行指令重排序,把第2,3步骤调换。从 JVM 的角度来说这种调换并不会影响最终实例的创建,但是在这段 Double Check 代码里面,在高并发的情况下有可能会发生问题:
- 线程 A 在调用
new Singleton()
的时候,刚执行第二步(把内存区域赋值给INSTANCE
变量,此时内存里面的成员变量都是默认值),还没到第三步(调用类的构造方法,给成员变量赋初始值)的时候,线程 B 也调用了getInstance
方法。
- 线程 B 判断
INSTANCE == null
,由于此时INSTANCE
不为空,所以直接返回了INSTANCE
对象,但是此时INSTANCE
才仅是半初始化状态(成员变量都是默认值),线程 B 拿到的其实是一个不完整的INSTANCE
对象,这种情况下是会出问题的。
使用
volatile
来修饰 INSTANCE
对象 ,就可以避免指令重排序的问题。但是在实际的生产中,上述的情况发生的其实很少,只有在极高的并发量的时候才会偶尔出现。如果程序并发量不高,不使用 volatile
关键字一般也不会出现问题。- Author:mcbilla
- URL:http://mcbilla.com/article/1e285c7d-7c1d-8067-a34e-cee64e718428
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!
Relate Posts