单例模式

来源:互联网 发布:c语言期末考试试题 编辑:程序博客网 时间:2024/05/23 22:49

设计模式-单例模式

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

有时候,我们要求一个类仅有一个实例,这种场景下单例模式就可以派上用场。先看下单例模式的类图。

UML类图

这里写图片描述

单例模式的关键是构造函数定义为私有的,防止在外部实例化该类对象,并提供一个获取单例对象的公共方法。

代码实现

单例模式实现可分为单线程和多线程的,也即是否线程安全的。

单线程的单例

/** * <p>文件描述: 单线程的单例</p> * * @Author luanmousheng * @Date 17/6/25 下午2:48*/public class Singleton {    private static Singleton instance;    private Singleton() {    }    public static Singleton getInstance() {        if (instance == null) {            instance = new Singleton();        }        return instance;    }}

多线程的单例

多线程下创建单例不是线程安全的,有两种方式可以解决这个问题,DCL加锁机制和静态初始化机制。加锁保证了同时只有一个线程去创建单例,静态初始化是借助了类加载机制。

双重检查锁定(Double Check Locking, DCL)
/** * <p>文件描述: 双重检查锁定机制:DCL</p> * * @Author luanmousheng * @Date 17/6/25 下午2:59*/public class Singleton {    private static volatile Singleton singleton;    private Singleton() {    }    public static Singleton getInstance() {        if (singleton == null) {            synchronized (Singleton.class) {                if (singleton == null) {                    singleton = new Singleton();                }            }        }        return singleton;    }}

双重检查锁定的第一次检查,是为了判断实例是否已经存在,只有当实例不存在才会去创建,否则每次调用getInstance方法都要去做同步锁定,效率太低。

在第一次检查和第二次检查之间的同步块,是为了保证同时只有一个线程创建实例对象。

那为何要有第二次检查呢?假如一开始单例实例不存在,A和B线程都通过了第一次检查,假设线程A竞争到了同步锁,进入到了同步块内,直接创建实例。当线程A离开同步块后,线程B拿到同步锁并进入到同步块,也会创建实例,那就创建了两次实例。因此第二次检查是必要的。

静态变量singleton为何被声明为volatile的?singleton = new Singleton()不是原子的操作,将singleton声明为volatile,禁止了指令重排序,防止其他线程拿到了还未初始化完成的对象引用(CPU可能先给singleton赋值为对象的地址,然后去初始化对象,此时其他线程就可能拿到未初始化完成的对象引用)。

静态初始化机制

静态初始化机制包括类静态变量的初始化和静态块的初始化

静态变量初始化
/** * <p>文件描述: 静态变量初始化</p> * * @Author luanmousheng * @Date 17/6/25 下午3:33*/public class Singleton {    private static Singleton instance = new Singleton();    private Singleton() {    }    public static Singleton getInstance() {        return instance;    }}

这种方法利用了类加载机制,类加载的时候就创建了对象。

静态块初始化
/** * <p>文件描述: 静态内部类机制</p> * * @Author luanmousheng * @Date 17/6/25 下午3:40*/public class Singleton {    /**     * 静态内部类     */    private static class SingletonHolder {        private static final Singleton instance = new Singleton();    }    /**     * <p>功能描述: 只有真正调用该方法时才会去初始化instance</p>     *     * @Author luanmousheng     * @Date 17/6/25 下午3:42    */    public static Singleton getInstance() {        return SingletonHolder.instance;    }}

这种方式最大的优点是延迟初始化,只有在需要的时候才会去创建实例。

参考:

  1. 大话设计模式. 程杰著
原创粉丝点击