设计模式 01 单例模式

Java单例模式详解:饿汉式、懒汉式与枚举实现

单例模式(Singleton Pattern)属于创建型模式

概述

单例就是只有一个实例对象,即在整个程序中,同一个类始终只有一个对象进行操作。这样可以极大的减少内存开支和系统的性能开销,因此应用十分广泛。比如数据库连接类,实际上只需要创建一个对象或是直接使用静态方法就可以了,没必要去创建多个对象。

这种模式提供了一种创建对象的最佳方式,让类负责创建自己的对象,同时确保只有单个对象被创建。这个类需要提供访问其唯一对象的方式,且可以直接访问,不需要实例化该类的对象。

注意点:

  • 为保证只能由自己创建对象,单例类必须构造方法私有化
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其他对象提供这一实例。

经过多年的演进,单例模式有诸多实现方式,下面逐个介绍。

代码实现

饿汉式

饿汉式单例是最普通的单例模式写法,由于在类加载时就创建对象,保证了线程的安全。这种方式比较常用,但容易产生垃圾对象,对空间的消耗较大。

public class Singleton {
   
   
    /**
     * 单例模式的核心,构造方法私有化
     */
    private Singleton() {
   
   

    }

    /**
     * 用于全局引用的唯一单例对象,在一开始就创建好
     */
    private final static Singleton INSTANCE = new Singleton();

    /**
     * 获取全局唯一的单例对象
     * @return 实例对象
     */
    public static Singleton getInstance() {
   
   
        return INSTANCE;
    }
}

这种方式最大的问题就是浪费内存,因为创建的对象程序不一定用得到,如果创建了没用到,就是一种浪费。这种方式由于不存在线程安全问题, 因此不用加锁,效率较高,以空间换时间

想要避免这种浪费,自然就想到在使用的时候才创建对象,这样就诞生了懒汉式

懒汉式

普通

懒汉式单例就是在类加载时不创建对象,用到的时候才创建对象。

public class Singleton {
   
   
    /**
     * 单例模式的核心,构造方法私有化
     */
    private Singleton() {
   
   

    }
    
    /**
     * 在一开始先不进行对象创建
     */
    private static Singleton INSTANCE;

    /**
     * 获取全局唯一的单例对象
     * @return 单例对象
     */
    public static Singleton getInstance() {
   
   
        // 如果实例为空,那么就进行创建,不为空说明已经创建过了,就直接返回,保证单例
        if (INSTANCE == null) {
   
       
            INSTANCE = new Singleton();
        }
        return INSTANCE;
    }
}

这种方式最大的问题就是在多线程情况下不安全,比如这样调用:

for (int i = 0; i < 10; i++) {
   
   
    new Thread(()->{
   
   
        Singleton.getInstance();
    }).start();
}

多线程环境下,非空判断容易失效,造成创建多个实例,违背了单例的初衷。为了避免这一问题,就不得不加锁,这样效率就会降低,以时间换空间

为了避免线程安全问题,还得进行一些改进:

// 方法添加 synchronized 关键字加锁
public static synchronized Singleton getInstance(){
   
      
    if(INSTANCE == null) {
   
   
        INSTANCE = new Singleton();
    }
    return INSTANCE;
}

既然多个线程要调用,那么就直接加一把锁,在方法上添加 synchronized 关键字即可,这样同一时间只能有一个线程进入了。

虽然这样简单粗暴,但是在高并发的情况下,效率肯定是比较低的,可以再进行优化:

public static Singleton getInstance(){
   
   
    // 规避多线程情况
    if(INSTANCE == null) {
   
   
        // 只对赋值这一步进行加锁,提升效率
        synchronized (Singleton.class) {
   
       
            INSTANCE = new Singleton();   
        }
    }
    return INSTANCE;
}

不过这样还不完美,因为这样还是有可能多个线程同时判断为 null 而进入等锁的状态。

所以,还得加一层内层判断:

public static Singleton getInstance(){
   
   
    if(INSTANCE == null) {
   
   
        synchronized (Singleton.class) {
   
   
            // 内层检测以实现单例
            if(INSTANCE == null) {
   
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天航星

感谢你的鼓励和认可

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值