设计模式——单例模式

1. 单例模式介绍 【设计模式——单例模式】单例模式提供了一种创建对象的最佳方式。
由某类负责创建自己的对象,并提供该对象的访问方式,以确保该对象是该类唯一的实例。
(1) 适用情况:
存在一个全局使用的类被频繁的创建和销毁时,可以考虑使用单例模式。
(2) 优点:
在堆中有且仅有一个实例,避免了频繁创建销毁以及保存的内存开销。
(3) 缺点:
没有接口,不能继承。
它在类的内部创建了自己的对象,这与单一职责原则冲突,它本来不应该关心外部是怎么实例化它的。
2. 单例模式实现 (1) 懒汉式(线程不安全)
先来看一种最简单的单例模式:

public class Singleton { private static Singleton instance; // 私有构造方法 private Singleton() { }public static Singleton getSingleton() { if (instance == null) { instance = new Singleton(); } return instance; } }

为什么叫懒汉,因为它不急着创建实例,而是等到判定为空的时候才去创建
它最大的问题就是线程不安全,因为可能会有多个线程判定instance == null,从而创建多个实例,严格意义上说,它并不能算单例模式。
(2) 懒汉式(线程安全)
上边的实现是线程不安全的,那给getSingleton方法增加synchronized不就行了吗:
public class Singleton { private static Singleton instance; // 私有构造方法 private Singleton() { }public static synchronized Singleton getSingleton() { if (instance == null) { instance = new Singleton(); } return instance; } }

这样做确实是可以保证线程安全的,但是加锁会影响效率
其实除了第一次需要创建之外,后续的加锁就没有意义了
(3) 饿汉式
饿汉式指的是在类加载的时候,就直接创建了实例:
public class Singleton { private static Singleton instance = new Singleton(); // 私有构造方法 private Singleton() { }public static Singleton getSingleton() { return instance; } }

这是比较常用的方式,也可以保证线程安全
但是在类加载的时候就实例化的话,万一它很消耗资源,就有点浪费内存
(4) 双重校验锁
使用双重校验锁的方式能同时保证线程安全和高性能:
public class Singleton { // 注意这里的volatile private volatile static Singleton instance; // 私有构造方法 private Singleton() { }public static Singleton getSingleton() { // 第一次判断是否为空,为空才创建,否则直接返回 if (instance == null) { synchronized (Singleton.class) { // 第二次判断是否为空,防止已被其他线程实例化之后重复实例化 if (instance == null) { instance = new Singleton(); } } } return instance; } }

这种方式中判断了两次instance == null:
第一次判断是否为空,为空才创建,否则直接返回
第二次判断是否为空,防止已被其他线程实例化之后重复实例化
这里的关键是在声明instance变量的时候,增加了volatile关键字
volatile有两个作用:
  1. 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。但如果对该变量执行的是非原子操作,它不能保证线程安全
  2. 禁止指令重排序

    推荐阅读