单例模式(Singleton)——设计模式学习笔记

单例模式概念:

单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的一个类只有一个实例。即一个类只有一个对象实例。(参考百度百科)

实现思路:

  1. 构造方法私有化
  2. 声明一个静态私有的本类对象
  3. 给外部提供一个静态方法获取对象实例

实现方式:

实现方式有很多种本文提出五种:
一. 饿汉模式

/**
 *  饿汉模式
 * 类被加载后对象被创建
 *
 * 缺点:没有懒加载效果
 *
 * @author lixiang
 * @date 2019年04月16日 - 11:05
 * @history 2019年04月16日 - 11:05 lixiang create.
 */
public class HungrySingleton {

    private static HungrySingleton hungrySingleton = new HungrySingleton();

    private HungrySingleton() {
    }

    public static HungrySingleton getInstance() {
        return hungrySingleton;
    }
}

二. 懒汉模式

/**
  * 懒汉模式
  * 调用getInstance方法后才创建对象 (延时加载、懒加载)
  * 缺点:存在线程安全问题(具体见下文双重校验锁分析)
  *
  *  @author lixiang
  *  @date 2019年04月16日 - 11:13
  *  @history 2019年04月16日 - 11:13 lixiang create.
 */
public class LazySingleton {

    private static LazySingleton lazySingleton = null;

    private LazySingleton() {
    }

    public static LazySingleton getInstance() {
       if (null == lazySingleton) {
           lazySingleton = new LazySingleton();
       }
       return lazySingleton;
    }
}

三. 双重校验锁

**
 *  双重校验锁
 *  较常用
 *  优点:线程安全
 *       效率较高
 *       延迟加载
 *
 * @author lixiang
 * @date 20190416- 11:14
 * @history 20190416- 11:14 lixiang create.
 */
public class DoubleCheckSingleton {

    private static volatile DoubleCheckSingleton doubleCheckSingleton = null;

    private DoubleCheckSingleton() {
    }

    public static DoubleCheckSingleton getInstance() {
    	// 第一次校验
       if (null == doubleCheckSingleton) {
           synchronized(DoubleCheckSingleton.class) {
           		// 第二次校验
               if (null == doubleCheckSingleton) {
                   doubleCheckSingleton = new DoubleCheckSingleton();
               }
           }
       }
       return doubleCheckSingleton;
    }

这里重点说下双重校验锁模式,为什么要进行双重校验?为什么加volatile 关键字?
容我给你一一道来:1.如果我们去掉第一个 if (null == doubleCheckSingleton)校验,聪明的你肯定发现了代码是可以在并发情况下正常运行的,因为有synchronized来保证我们的线程安全。既然用了synchronized那必然会损耗性能,因为一旦有线程进入了 synchronized(DoubleCheckSingleton.class){}块那后续线程就需要阻塞等待。那我们如何在此基础上来提高性能呢?于是就有了第一个 if (null == doubleCheckSingleton)校验。
2.如果我们去掉第二个if (null == doubleCheckSingleton)校验,此时代码就有问题了,问题出在哪里呢?
假设当doubleCheckSingleton == null时来了两个线程A、B同时到达第一个 if (null == doubleCheckSingleton)校验,毫无疑问顺利通过校验执行if里面的方法,这时因为synchronized的缘故只能有一个线程进入另一个线程等待。假设A线程进入了synchronized块执行了doubleCheckSingleton = new DoubleCheckSingleton();结束后,B线程再次执行doubleCheckSingleton = new DoubleCheckSingleton();显然这违背了我们单例模式的初衷。
3.如果我们去掉volatile关键字,此时代码也是有问题的。在JVM中创建对象和赋值操作是分开进行的,也就是说doubleCheckSingleton = new DoubleCheckSingleton();语句是分三步执行的:

1). 分配内存空间。
2). 初始化对象。
3). 将doubleCheckSingleton 指向刚分配好的内存地址。

但是JVM并不保证这三个操作的先后顺序,也就是说有可能JVM会按照1–>3–>2来执行。这样就可能出错了,我们以A、B两个线程为例:
a) A线程先进来,由于doubleCheckSingleton == null,A线程进入了第一个if判断,然后进入synchronized块,并执行doubleCheckSingleton = new DoubleCheckSingleton();
b) 由于JVM内部的优化机制,JVM先分出了一些内存,并赋值给doubleCheckSingleton (注意此时JVM还没有初始化这个实例),此时A线程切换到B线程。
c) B进行第一个if判断,由于此时doubleCheckSingleton != null,因此它马上 return doubleCheckSingleton;并将结果返回给调用该方法的程序。这时B线程打算使用doubleCheckSingleton 实例,却发现它没有被初始化,于是错误发生了。

volatile关键字足矣另写一篇博客,本文不对其进行具体展开了,这里我们利用的是:volatile 变量的写操作,不允许和它之前的读写操作打乱顺序;对 volatile 变量的读操作,不允许和它之后的读写乱序。 如果有时间我再另写一篇博客深入探究一下volatile关键字。

四. 静态内部类

/**
 *
 * 静态内部类
 *
 * 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,
 * 在类进行初始化时,别的线程是无法进入的
 *
 * 优点:懒加载效果
 *      线程安全
 *      效率高
 *
 * @author lixiang
 * @date 2019年04月16日 - 14:52
 * @history 2019年04月16日 - 14:52 lixiang create.
 */
public class StaticInnerSingleton {

    private StaticInnerSingleton() {
    }

    private static class SingletonHolder {
        private static final StaticInnerSingleton STATIC_INNER = new StaticInnerSingleton();
    }

    public static StaticInnerSingleton getInstance() {
        return SingletonHolder.STATIC_INNER;
    }
}

五. 枚举类

/**
 *  枚举类
 *  优点:避免多线程同步问题
 *       防止反序列化重新创建新的对象
 *       
 * @author lixiang
 * @date 2019年04月16日 - 14:55
 * @history 2019年04月16日 - 14:55 lixiang create.
 */
public enum EnumSingleton {
    ENUM_SINGLETON;
}

总结是对学习的检验、沉淀与升华,如果有问题还请不吝赐教!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值