0.参考资料


点击查看【processon】
ProcessOn链接


1.概述

  • 保证一个类仅有一个实例,并提供一个该实例的全局访问点。 — 《设计模式》GoF

    1.1动机

    • 在软件系统中,经常有这样一-些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。
    • .如何绕过常规的构造器,提供-一种机制来保证一 个类只有- 一个实例?
      • 这应该是类设计者的责任 ,而不是使用者的责任。

        1.2结构

        image.png

        2.要点总结

  1. Singleton模式中的实例构造器可以设置为 protected 以允许子类派生。
  2. Singleton模式一般不要支持拷贝构造函数和Clone接口,因为这有可能导致多个对象实例,与Singleton模式的初衷违背。
  3. 如何实现多线程环境下安全的Singleton?注意对双检查锁的正确实现。

    x.问题

    x.1 Synchronized锁双重校验法 内存中指令重排问题

  • 双重校验(double check)示例:

    1. public static Singleton getSingleton() {
    2. private static volatile Singleton instance;
    3. if (instance == null) {
    4. synchronized (Singleton.class) {
    5. if (instance == null) {
    6. instance = new Singleton();
    7. }
    8. }
    9. }
    10. return instance ;
    11. }
  • 如上,在同步代码块的内部和外部都判断了instance == null,这时因为,可能会有多个线程同时进入到同步代码块外的if判断中,如果在同步代码块内部不进行判空的话,可能会初始化多个实例。

  • 问题所在:
    • instance = new Singleton(); 并不是原子性的操作。
    • new Object(): 汇编后的三句指令:

1.分配内存
2.new 对象
3.把内存地址给引用赋值

  1. - instance指向分配地址时,instance不为空<br />但是,23步之间,可能会被重排序,造成创建对象顺序变为1-3-2. 若紧接着另外一个线程来调用getInstance,取到的就是状态不正确的对象,程序就会出错。
  • 解决方法:
    • 使用volatile:防止指令重排,保证有序性.
      1. public class Singleton{
      2. private static volatile Singleton instance;
      3. public static Singleton getSingleton() {
      4. if (instance == null) {
      5. synchronized (Singleton.class) {
      6. if (instance == null) {
      7. instance = new Singleton();
      8. }
      9. }
      10. }
      11. return instance ;
      12. }
      13. }