Table of Contents

一、单例模式介绍

1. 定义

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

2. 单例模式的作用

在我们的系统中,有一些对象其实我们只需要一个,比如说:线程池、缓存、对话框、注册表、日志对象、显卡 等设备驱动程序的对象。事实上,这一类对象只能有一个实例,如果制造出多个实例就可能会导致一些问题的产生,比如:程序的行为异常、资源使用过量、或者不一致性的结果。

简单来讲,单例模式可以带来的好处有两点:

  • 对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销;
  • 由于 new 操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻 GC 压力,缩短 GC 停顿时间。

为什么不使用全局变量确保一个类只有一个实例呢?

使用时创建、避免全局变量的使用

这部分可以参考单例模式好处

3. 单例模式的实现

实现要点

单例模式要求类能够有返回对象一个引用(永远是同一个)和一个获取该实例的方法(必须是静态方法)。

单例的实现主要通过以下三个步骤:

  • A. 将类的构造方法定义为私有方法。这样其他类的代码就无法通过调用该类的构造方法来实例化该类的对象,只能通过该类提供的静态方法来得到该类的唯一实例。
  • B. 定义一个私有的类的静态变量。
  • C. 提供一个共有的获取实例的静态方法。

构建方式

通常单例模式在 java 语言中,有两种构建方式:

  • 饿汉模式:指全局的单例实例在类加载时构建
  • 懒汉方式:指全局的单例实例在第一次使用被使用时构建

不管是哪种构建方式,他们都存在下面几点相似处:

  • private 级别的构造函数,保证只有一个实例
  • instance 成员变量和 uniqueInstance 方法都是 static 的,保证直接通过类来获取

实现

  1. 饿汉式-线程安全

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    public class Singleton {
    
        private static Singleton uniqueInstance = new Singleton();
    
        private Singleton() {
        }
    
        public static Singleton getUniqueInstance() {
            return uniqueInstance;
        }
    }
    
  2. 懒汉式-线程不安全

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    public class Singleton {
    
        private static Singleton uniqueInstance;
    
        private Singleton() {
        }
    
        public static Singleton getUniqueInstance() {
            if (uniqueInstance == null) {
                uniqueInstance = new Singleton();
            }
            return uniqueInstance;
        }
    }
    
  3. 懒汉式-线程安全

    加锁

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    public class Singleton {
    
        private static Singleton uniqueInstance;
    
        private Singleton() {
        }
    
        public static synchronized Singleton getUniqueInstance() {
            if (uniqueInstance == null) {
                uniqueInstance = new Singleton();
            }
            return uniqueInstance;
        }
    }
    
  4. 双重校验锁-线程安全

    两重的 if 校验

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    public class Singleton {
    
        private volatile static Singleton uniqueInstance;
    
        private Singleton() {
        }
    
        public static Singleton getUniqueInstance() {
            if (uniqueInstance == null) {
                synchronized (Singleton.class) {
                    if (uniqueInstance == null) {
                        uniqueInstance = new Singleton();
                    }
                }
            }
            return uniqueInstance;
        }
    }