单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
一、单例模式介绍
- 从上面的定义中我们可以得知单例模式具有以下几点特征:
- 单例类只能有一个实例
- 单例类必须自己创建自己的唯一实例(不需要外部实例化该类对象)
- 单例类必须给所有其他对象提供这一实例(提供访问的方式)
- 构造函数是私有的(与第二点对应,关键)
- 优点:
- 减少内存开销
- 避免资源多重占用
- 缺点:
- 没有接口
- 不能继承
- 一个类应该只关心内部逻辑,而不关心外面怎么样来实例化
- 使用场景:
- 要求生产唯一序列号(比如说支付订单号)
- WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来
- 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等
- 注意:
- getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化
二、单例模式的实现
单例模式有以下几种实现方式:懒汉式(分为线程安全、线程不安全两种)、饿汉式、双检索(DCL)、静态内部类、枚举。
- 懒汉式(线程不安全)
- 是否延迟初始化:是
- 线程不安全
- 代码实现:
public class Singletion { private static Singletion instance; // 构造对象私有,也就是说不要外部实例化对象 private Singletion {} // 获取唯一可用对象(获取实例的唯一途径,对于外部来说) public static Singletion getInstance() { if (instance == null) { instance = new Singletion(); } return instance; } }
- 懒汉式(线程安全的实现方式)
- 延迟初始化
- 线程安全
- 但是效率很低
- 代码实现:
public class Singletion { private static Singletion instance; // 构造对象私有,也就是说不要外部实例化对象 private Singletion {} // 方法加锁 (synchronized) public static synchronized Singletion getInstance() { if (instance == null) { instance = new Singletion(); } return instance; } }
- 饿汉式
- 非延迟初始化
- 线程安全
- 执行效率提高
- 但是类加载时就初始化,浪费内存,而且容易产生垃圾对象
- 代码实现:
public class Singletion { private static Singletion instance = new Singletion(); // 构造对象私有,也就是说不要外部实例化对象 private Singletion {} // 直接返回对象实例 public static Singletion getInstance() { return instance; } }
- DCL双检索机制
- 延迟初始化
- 线程安全
- 这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
- 代码实现:
public class Singletion { private volatile static Singletion instance; // 构造对象私有,也就是说不要外部实例化对象 private Singletion {} // 直接返回对象实例 public static Singletion getInstance() { if (instance == null) { synchronized (Singletion.class) { if (instance == null) { instance = new Singletion(); } } } return instance; } }
- 静态内部类(登记式)
- 延迟初始化
- 线程安全
- 这种方式只适用于静态域的情况(注意与第三种的比较)
- 第 3 种方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。
- 代码实现:
public class Singletion { public static class SingletionHolder { private static final Singletion INSTANCE = new Singletion(); } // 构造对象私有,也就是说不要外部实例化对象 private Singletion {} // 直接返回对象实例 public static final Singletion getInstance() { return SingletionHolder.INSTANCE ; } }
- 枚举
- 非延迟初始化
- 线程安全
- 自动支持序列化机制,绝对防止多次实例化
- 代码实现:
public enum Singleton { INSTANCE; public void whateverMethod() {} }
参考教程:菜鸟教程BUNOOB