DesignPattern - 单例模式【创建型】
# 一、单例模式介绍
通过单例模式可以保证系统中,应用该模式的类只有一个对象实例。
好处:
- 节省内存(没必要存在 多个功能相同的对象)
- 提高性能(有些对象的创建很耗系统资源)
分类:
- 饿汉:提前创建对象(
class
一加载就创建) - 懒汉:延迟创建对象(调用
getInstance()
时再创建)
- 饿汉:提前创建对象(
实现步骤:
- 私有化构造函数
- 提供获取单例的方法
# 二、单例模式代码实现
# 1、饿汉式
- 饿汉方式:提前创建好对象
- 优点:实现简单,没有多线程同步问题
- 缺点:类一加载就会创建,不管有没有使用,instance 对象一直占着内存
/**
* 单式模式:饿汉式
*
* @author GitLqr
*/
public class SingletonHungry {
private static SingletonHungry instance = new SingletonHungry();
private SingletonHungry() {
}
public static SingletonHungry getInstanceHungry() {
return instance;
}
}
# 2、懒汉式
- 懒汉方式:延迟创建对象
- 优点:需要用到时才会创建对象,规避不必要的内存浪费
- 缺点:可能需要考虑有多线程同步问题
# 1)DCL (+ volatile) 方式
DCL,即双重检查锁定 (Double-Checked-Locking)
synchronized
前第一次判空:避免不必要的加锁同步,提高性能synchronized
后第二次判空:避免出现多线程安全问题volatile
修饰 instance:避免指令重排序问题
/**
* 单例模式:懒汉式 (DCL + volatile)
*
* @author GitLqr
*/
public class SingletonLazy {
private static volatile SingletonLazy instance;
private SingletonLazy() {
}
public static SingletonLazy getInstance() {
if (instance == null) {
synchronized (SingletonLazy.class) {
if (instance == null) {
instance = new SingletonLazy();
}
}
}
return instance;
}
}
# 2)静态内部类 方式
Holder
静态内部类:外部类加载时,并不会直接导致静态内部类被加载,在调用getInstance()
时才会触发该静态内部类被加载,所以,可以延时执行。Holder.instance
static 字段:同一个加载器下,一个类型只会初始化一次,故天然的线程安全。
/**
* 单例模式:懒汉式 (静态内部类)
*
* @author GitLqr
*/
public class SingletonLazyClass {
private static class Holder {
private static SingletonLazyClass instance = new SingletonLazyClass();
}
public static SingletonLazyClass getInstance() {
return Holder.instance;
}
private SingletonLazyClass() {
}
}
注意:
静态内部类
相比DCL
代码简洁很多,即有饿汉式的优势,又可以做到延时初始化,看似很完美,但其有一个致命问题,即无法传参,所以,实际开发中,要根据实际情况来选择其中一种实现方式。
# 3)枚举 方式
- 枚举无法 new,也就无须私有化构造函数,相比 静态内部类 方式要简洁的多
- 枚举实例创建是线程安全的
/**
* 单例模式:懒汉式 (枚举)
*
* @author GitLqr
*/
public enum SingletonEnum {
INSTANCE;
// 枚举与普通类一样,可以拥有字段和方法
public void method() {
// TODO
}
}
注意:缺点跟
静态内部类
方式一样,外部无法传参。
# 三、DCL 单例模式的演进
# 阶段一:简单的对象判空
- 缺点:线程不安全,多线程下存在安全问题
- 解决办法:加锁
public class SingletonLazy {
private static SingletonLazy instance;
private SingletonLazy() {
}
public static SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
# 阶段二:通过加锁 synchronized 保证单例
- 缺点:采用 synchronized 对方法加锁有很大的性能开销
- 解决办法:锁粒度不要这么大
public class SingletonLazy {
private static SingletonLazy instance;
private SingletonLazy() {
}
public static synchronized SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
# 阶段三:DCL 双重检查锁定 (Double-Checked-Locking)
- 优点:在多线程情况下保持高性能
- 缺点:不安全,instance = new SingletonLazy(); 不是原子性操作,这行代码为如下 3 步:
- 分配内存空间
- 在空间内创建对象
- 对对象赋值给引用
- 原因:因为 JVM 指令重排序问题,可能导致线程中会按 1->3->2 的顺序执行(JVM 认为 2 和 3 没有先后顺序),其中步骤 3 会把 线程副内存中 的值写入主内存,其他线程就会读取到 instance 最新的值,但因为步骤 2 还没有执行,此时这个 instance 是不完全的对象,这时其他线程中使用这个不完全的 instance 就会出错。
- 解决办法:使用 volatile 关键字
public class SingletonLazy {
private static SingletonLazy instance;
private SingletonLazy() {
}
public static SingletonLazy getInstance() {
if (instance == null) {
synchronized (SingletonLazy.class) {
if (instance == null) {
instance = new SingletonLazy();
}
}
}
return instance;
}
}
# 阶段四:DCL + volatile
- volatile:是 java 提供的关键字,可以禁止指令重排序
public class SingletonLazy {
private static volatile SingletonLazy instance;
private SingletonLazy() {
}
public static SingletonLazy getInstance() {
if (instance == null) {
synchronized (SingletonLazy.class) {
if (instance == null) {
instance = new SingletonLazy();
}
}
}
return instance;
}
}
- 01
- Flutter - 危!3.24版本苹果审核被拒!11-13
- 02
- Flutter - 轻松搞定炫酷视差(Parallax)效果09-21
- 03
- Flutter - 轻松实现PageView卡片偏移效果09-08