元月's blog 元月's blog
首页
  • 基础
  • 并发编程
  • JVM
  • Spring
  • Redis篇
  • Nginx篇
  • Kafka篇
  • Otter篇
  • Shardingsphere篇
  • 设计模式
  • MySQL
  • Oracle
  • 基础
  • 操作系统
  • 网络
  • 数据结构
  • 技术文档
  • Git常用命令
  • GitHub技巧
  • 博客搭建
  • 开发工具
更多

元月

临渊羡鱼,不如退而结网
首页
  • 基础
  • 并发编程
  • JVM
  • Spring
  • Redis篇
  • Nginx篇
  • Kafka篇
  • Otter篇
  • Shardingsphere篇
  • 设计模式
  • MySQL
  • Oracle
  • 基础
  • 操作系统
  • 网络
  • 数据结构
  • 技术文档
  • Git常用命令
  • GitHub技巧
  • 博客搭建
  • 开发工具
更多
  • 设计模式

    • 设计模式简介
    • 设计模式之单例模式
      • 一、简介
      • 二、实现方式
        • 1、懒汉式,线程安全
        • 2、饿汉式
        • 3、双检锁/双重校验锁(DCL,即 double-checked locking)
        • 4、静态内部类
        • 5、枚举
      • 三、应用
        • 1、Spring中的单例模式
      • 四、思维导图
    • 设计模式之工厂模式
    • 设计模式之原型模式
    • 设计模式之建造者模式
    • 设计模式之适配器模式
    • 设计模式之桥接模式
    • 设计模式之组合模式
    • 设计模式之装饰器模式
    • 设计模式之外观模式
    • 设计模式之享元模式
    • 设计模式之代理模式
    • 设计模式之责任链模式
    • 设计模式之命令模式
    • 设计模式之解释器模式
    • 设计模式之迭代器模式
    • 设计模式之中介者模式
    • 设计模式之备忘录模式
    • 设计模式之观察者模式
    • 设计模式之状态模式
    • 设计模式之策略模式
    • 设计模式之模版模式
    • 设计模式之访问者模式
    • 设计模式辨析篇
  • 高可用

  • 系统设计
  • 设计模式
元月
2022-08-02
目录

设计模式之单例模式

# 设计模式之单例模式

# 一、简介

保证一个类仅有一个实例,并提供一个全局访问点

image-20220802181900694

私有的静态成员变量、私有的构造方法、静态的访问实例方法。

# 二、实现方式

# 1、懒汉式,线程安全

它使用了synchronized关键字,但是效率低,因为99%的情况下不需要同步

public class Singleton {
    private static Singleton instance;
    private Singleton() {
    }
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
1
2
3
4
5
6
7
8
9
10
11

# 2、饿汉式

它基于类加载机制避免了多线程的同步问题

public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton() {
    }
    public static Singleton getInstance() {
        return instance;
    }
}
1
2
3
4
5
6
7
8

# 3、双检锁/双重校验锁(DCL,即 double-checked locking)

它采用了双锁机制,另外还使用了volatile 和 synchronized,安全并且在多线程情况下也能够保持高性能

public class Singleton {
    private volatile static Singleton singleton;
    private Singleton() {
    }
    public static Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {//避免多线程情况下创建多个实例问题
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

此处需要注意使用volatile,因为对象的创建并非原子的,可能指令重排 。

instance = new Singleton();我们可以分为3步完成(伪代码)

memory = allocate();//1.分配对象内存空间
instance(memory);//2.初始化对象
instance = memory;//3.instance指向刚分配的内存地址,此时instance!=null
1
2
3

由于步骤2和步骤3间可能会重排序,会导致使用了一个未初始化的对象,如下:

memory=allocate();//1.分配对象内存空间
instance=memory;//3.instance指向刚分配的内存地址,此时instance!=null,但是对象还没有初始化完成!
instance(memory);//2.初始化对象
1
2
3

由于步骤2和步骤3不存在数据依赖关系,而且无论重排前还是重排后程序的执行结果在单线程中并没有改变,因此这种重排优化是允许的。但是指令重排只会保证串行语义的执行的一致性(单线程),但并不会关心多线程间的语义一致性。所以当一条线程访问instance不为null时,由于instance实例未必已初始化完成,也就造成了线程安全问题。那么该如何解决呢,很简单,我们使用volatile禁止instance变量被执行指令重排优化即可。

//禁止指令重排优化 
private volatile static Singleton  singleton;
1
2

# 4、静态内部类

它基于类加载机制,只有通过显式调用 getInstance 方法时,才会开始装载 SingletonHolder ,从而实例化 instance

public class Singleton {
    private static class SingletonHolder {//静态内部类
        private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton() {
    }
    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
1
2
3
4
5
6
7
8
9
10

# 5、枚举

这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。 这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。 不能通过 reflection attack 来调用私有构造方法。

public enum Singleton {
    INSTANCE;
    public void whateverMethod() {
    }
}
1
2
3
4
5

# 三、应用

# 1、Spring中的单例模式

Spring中的单例bean使用了单例模式,采用了双重校验锁

/**
  * 单例对象的缓存,一级缓存
 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
				//首先通过名字查找这个单例bean存在不存在
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                //查看缓存中是否存在这个bean实例
                singletonObject = this.earlySingletonObjects.get(beanName);
                //如果这个时候的bean实例还是为空并且允许懒加载
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 四、思维导图

image-20220802181900694

#设计模式
设计模式简介
设计模式之工厂模式

← 设计模式简介 设计模式之工厂模式→

最近更新
01
otter二次开发-支持按目标端主键索引Load数据
08-03
02
mvnw简介
06-21
03
gor流量复制工具
06-03
更多文章>
Theme by Vdoing | Copyright © 2022-2024 元月 | 粤ICP备2022071877号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式