java单例模式(饿汉模式、懒汉模式)

单例模式

一、什么是单例模式

java中单例模式是一种常见的设计模式,单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

啥是设计模式?

​ 设计模式好比象棋中的 “棋谱”。红方当头炮, 黑方马来跳。针对红方的一些走法, 黑方应招的时候有一些固定的套路。按照套路来走局势就不会吃亏。

​ 软件开发中也有很多常见的 “问题场景”。针对这些问题场景, 大佬们总结出了一些固定的套路。按照这个套路来实现代码, 也不会吃亏。

二、单例模式的特点

  • 单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其他对象提供这一实例。

三、单例模式的实现形式

1.理解饿汉模式、懒汉模式

以现实中吃完饭洗碗为例:

  • 饿汉模式:
    • 一吃完饭就把所有的碗洗干净,下次吃饭直接就可以使用洗好了的碗
    • 在代码中就是提前把对象创建好,外界直接使用
  • 懒汉模式:
    • 吃完饭之后并不忙着洗碗,下次需要碗的时候,要用几个碗再去洗几个碗
    • 在代码中并没有提前创建好对象,而是外界需要对象的时候再来创建

2.实现饿汉模式

class Singleton {
    // 创建一个静态的变量,用来创建自己的对象
    //注意:此处必须是private修饰,不然外界可以直接使用,每一次的调用都会产生一个对象
    private static Singleton instance = new Singleton();

    // 获取到实例的方法
    // 外界通过这个方法来获取已经提供好了的对象
    // 保证每次获取的对象都是同一个
    public static Singleton getInstance() {
        return instance;
    }

    // 禁止外部 new 实例
    // 单例模式,也就只能有一个对象,设置成private,只能在类内部使用,不允许外界去创建对象
    private Singleton() { }
}

饿汉模式不论是单线程还是多线程,都是线程安全的,因为饿汉模式是获取已经创建好了的对象,外界只能获取同一个对象。

测试:

public static void main(String[] args) {
    Singleton s1 = Singleton.getInstance();
    Singleton s2 = Singleton.getInstance();
    //结果为true,说明获取的对象确实是一样的
    System.out.println(s1==s2);
}

image-20230324191423044

3.实现懒汉模式

单线程版本

class SingletonLazy {
    //创建了对象,但没有new
    private static SingletonLazy instance = null;
    
    private Singleton() {}
    
    //在方法内部,判断对象是否创建好了,如果没有再创建
    public static SingletonLazy getInstance() {
        if (instance == null) {
            instance = new Singleton();
       }
        return instance;
   }
}

局限:这种版本对于单线程还是可以使用的,但是运用到多线程就不行了。线程1发现对象不存在,就会去创建对象;线程2在线程1发现但还没有完全创建时也进来了,这样就会导致线程1和线程2都去创建不同的对象。

多线程版本

1.单线程版本的getInstance()不是原子操作,我们可以通过加锁来保证线程的安全性

    public static SingletonLazy getInstance() {
        synchronized (SingletonLazy.class) {
                if (instance == null) {
                    instance = new SingletonLazy();
                }
            }
        return instance;
   }

2.观察getInstance()方法,不难发现,不论是没有创建好对象,还是已经创建好了,每次都要排队进来进行判断对象是否存在

解决办法:使用双重 if 判定, 降低锁竞争的频率,只有第一次会进来创建对象,其他时候就会直接跳过

    public static SingletonLazy getInstance() {
        if (instance == null) {
            synchronized (SingletonLazy.class) {
                if (instance == null) {
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }
  1. 为了避免指令重排序导致读取的 instance 出现偏差, 于是我们可以使用 volatile 来修饰instance
class SingletonLazy {
    volatile private static SingletonLazy instance = null;

    public static SingletonLazy getInstance() {        
        if (instance == null) {
            synchronized (SingletonLazy.class) {
                if (instance == null) {
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }

    private SingletonLazy() { }
}

public class ThreadDemo18 {
    public static void main(String[] args) {
        SingletonLazy s1 = SingletonLazy.getInstance();
        SingletonLazy s2 = SingletonLazy.getInstance();
        System.out.println(s1 == s2);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值