【大神之路】大话设计模式 - 单例模式

一、简介

单例模式,Singleton , Java 23 设计模式中创建型模式之一, 该模式定义为:

一个类只有一个实例,且该类能自行创建这个实例的一种模式。

其主要特点为:

  • 单例类有且只有一个对象
  • 对象必须由单例类自行创建(其实一些业务层框架(例如Spring) 中的对象默认也是单例的)
  • 单例类对外提供一个访问该单例的全局访问方式

二、实现方式

单例模式是所有设计模式中几乎最简单的一个。其实现方式主要包括两种:

  • 懒汉式
  • 饿汉式

每一种实现方式有各有优缺点,咱们来看下

三、饿汉式

/**
 * <单例模式 - 饿汉式>
 *
 * @author CoWitoSug
 * @createTime: 2020/4/15
 */
public class Singleton {
    // 单例模式自行创建对象
    private static final Singleton INSTANCE = new Singleton();
    // 私有化构造方法, 不允许外部创建当前类对象
    // 不要较真的告诉 小C 还有反射!
    private Singleton(){}
    // 对外提供一个访问该单例的全局访问方式
    public static Singleton getInstance(){
        return INSTANCE;
    }
}

饿汉式, 在声明对象属性的同时初始化属性,这样做的优点是, 不会出现任何多线程下的线程安全问题, 缺点也很明显, 没有实现 Lazy Initialize (延迟加载,在使用该对象时才创建之)。如果这个对象在整个JVM运行过程中一直不使用, 该对象也需要 “ 常驻内存 ”

四、懒汉式

/**
 * <单例模式 - 懒汉式>
 *
 * @author CoWitoSug
 * @createTime: 2020/4/15
 */
public class Singleton {
    
    private static Singleton instance;
    
    private Singleton(){}
    
    public static Singleton getInstance(){
        // 使用对象时 才创建对象, 实现了对象的延迟初始化
        if( null == instance){
            instance = new Singleton();
        }
        return instance;
    }
}

懒汉式 解决了 饿汉式的 延迟加载 问题, 但是, 随之也出现了更加严重的 线程安全问题。

在多线程下, 可能会出现多个对象 (线程A 进入 if , CPU 时间片分配给 线程B , 线程B 也进入if , 同时创建instance 对象。此时,线程A 重新获得 CPU 使用权, 继续创建 instance 对象,从而出现了多个对象 )。

五、懒汉式 - 线程安全版

/**
 * <单例模式 - 线程安全版>
 *
 * @author CoWitoSug
 * @createTime: 2020/4/15
 */
public class Singleton {

    private static Singleton instance;

    private Singleton(){}

    // 为方法加上 同步锁, 实现了多线程下的线程安全。
    public static synchronized Singleton getInstance(){
        if( null == instance){
            instance = new Singleton();
        }
        return instance;
    }
}

代码中加了 synchronized 同步锁, 保证了多线程下的安全性, 但是, 效率实在是太低!那么, 应该怎么办呢

六、懒汉式 - DC 模式

/**
 * <单例模式 - DC模式>
 *
 * @author CoWitoSug
 * @createTime: 2020/4/15
 */
public class Singleton {

    private static Singleton instance;

    private Singleton(){}

    public static  Singleton getInstance(){
        // 进行double check 双重检查。进一步优化效率
        if(null == instance){
            synchronized(Singleton.class){
                if( null == instance){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

为了提高效率, 对代码从以下两个方面进行了优化:

  • 去掉方法同步的关键字synchronized,对Singleton.class进行加锁操作, 降低锁的粒度, 减少系统开销
  • 使用 DC ( Double Check )。即 对于 instance对象 进行 两次 空值 检测。 如果对象不为空, 则不需要重新获取对象锁。

七、懒汉式 - volatile

感觉到了 DC 部分已经很完善了, 万无一失!但是, 很遗憾, DC 模式下 也不是绝对的安全, 这次不是出现多个对象, 而是可能会出现 半个 对象的情况, 因为: 对象的创建过程不是原子的! 也就是说在对象创建 “一半” 的时候可能就会被另一个线程 “ 返回 ”。 从而出现bug。所以, 完善后的代码为:

/**
 * <单例模式 - volatile>
 *
 * @author CoWitoSug
 * @createTime: 2020/4/15
 */
public class Singleton {
    
    // 为对象添加volatile关键字, 保证其原子性
    private static volatile  Singleton instance;
    
    private Singleton(){}
    
    public static  Singleton getInstance(){
        if(null == instance){
            synchronized(Singleton.class){
                if( null == instance){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

关注我的CSDN博客, 小C与您共成长!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值