设计模式 | 创建型模式 | 单例模式

2021-03-06  本文已影响0人  暴走的朝天椒

单例模式:确保一个类只有一个实例,并提供一个全局访问点。不管是采用懒汉式,还是饿汉式的实现方式,这个全局访问点是一样的。

单例模式的范围:目前Java里面实现的单例是一个虚拟机的范围。因为装载类的功能是虚拟机的,所以一个虚拟机在通过自己的ClassLoader装载饿汉式实现单例类的时候就会创建一个类的实例。当一个虚拟机中有多个ClassLoader,而且这些ClassLoader都装载某个类的话,就算这个类是单例,也会产生很多个实例。当一个机器上有多个虚拟机,那么每个虚拟机里面都应该至少有一个这个类的实例。

单例模式的调用顺序示意图:

饿汉式的调用顺序.png 懒汉式的调用顺序.png

饿汉式

饿汉式实现示例代码如下:

/**
 * 饿汉式
 * 类加载到内存后,就实例化一个单例,JVM保证线程安全
 * 简单实用,推荐使用!
 * 唯一缺点:不管用到与否,类装载时就完成实例化
 */
public class Singleton01 {
    /**
     * 定义一个私有静态变量用来存储创建好的类实例,保证了线程安全
     */
    private static Singleton01 INSTANCE = new Singleton01();

    /**
     * 定义一个私有的类构造器,防止外部创建类实例,可以在内部控制类实例的数目
     */
    private Singleton01(){}

    /**
     * 定义一个静态方法,为客户端提供访问类实例的方法
     * @return
     */
    public static Singleton01 getInstance(){
        return INSTANCE;
    }
}

饿汉式:在装载类的时候就创建了对象实例,因此,JVM保证了线程安全。
缺点:不管用不用,类装载时就完成了实例化。

懒汉式(线程不安全)

懒汉式实现示例代码如下:

/**
 * lazy loading
 * 也称懒汉式
 * 虽然达到了按需初始化的目的,但却带来线程不安全的问题
 */
public class Singleton02 {
    /**
     * 定义一个私有静态变量,用来存储类实例
     */
    private static Singleton02 INSTANCE;

    /**
     * 定义一个私有构造器,防止外部创建类实例,可以在内部创建类实例的数目
     */
    private Singleton02() {}

    /**
     * 定义一个静态方法,为客户端提供访问类实例的方法,线程不安全
     * @return
     */
    public static Singleton02 getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton02();
        }
        return INSTANCE;
    }
}

懒汉式:在装载对象的时候不创建对象实例,当第一次使用的时候,才会创建实例,在多线程情况下,判断INSTANCE == null会导致线程安全问题。

懒汉式(线程安全)

懒汉式实现示例代码如下:

/**
 * lazy loading
 * 也称懒汉式
 */
public class Singleton03 {
    /**
     * 定义一个静态变量,用来存储类实例
     */
    private static Singleton03 INSTANCE;

    /**
     * 定义一个私有的构造器,在内部控制创建类实例的数目
     */
    private Singleton03() {}

    /**
     * 用synchronized关键字定义一个静态方法,为客户端提供访问类实例的方法,线程安全,但也效率低下
     * @return
     */
    public static synchronized Singleton03 getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton03();
        }
        return INSTANCE;
    }
}

通过增加synchronized关键字到getInstance()方法中,解决了懒汉式的线程安全问题,但同时导致了效率低下。

懒汉式(双重检查加锁)

懒汉式实现示例代码如下:

/**
 * lazy loading
 * 也称懒汉式(双重检查加锁),在getInstance() 中减少使用同步
 */
public class Singleton05 {
    /**
     * volatile关键字确保在多个线程下正确的处理INSTANCE变量
     */
    private static volatile Singleton05 INSTANCE; //JIT

    /**
     * 创建私有的构造器,在内部控制创建类实例的数目
     */
    private Singleton05() {}

    /**
     * 用双重检查加锁的方式,为客户端提供访问类实例的方法,保证了线程安全,减少使用同步区块
     * @return
     */
    public static Singleton05 getInstance() {
        // 检查实例,若不存在,就进入同步区块
        if (INSTANCE == null) {
            synchronized (Singleton05.class) {
                // 进入同步区块后,在检查一次,如果仍为null,才创建实例
                if(INSTANCE == null) {
                    INSTANCE = new Singleton05();
                }
            }
        }
        return INSTANCE;
    }
}

静态内部类

静态内部类实现示例代码如下:

/**
 * 静态内部类方式
 * JVM保证单例
 * 加载外部类时不会加载内部类,这样可以实现懒加载
 */
public class Singleton06 {
    /**
     * 定义私有构造器
     */
    private Singleton06() {}

    /**
     * 定义私有静态内部类
     */
    private static class Mgr06Holder {
        private static Singleton06 INSTANCE = new Singleton06();
    }

    /**
     * 为客户端提供访问类实例的方法
     * @return
     */
    public static Singleton06 getInstance() {
        return Mgr06Holder.INSTANCE;
    }
}

枚举

枚举实现实例代码如下:

/**
 * 不仅可以解决线程同步,还可以防止反序列化。
 */

public enum Singleton07 {
    INSTANCE;
}
上一篇 下一篇

猜你喜欢

热点阅读