多线程案例——单例模式(饿汉模式和懒汉模式)

一、单例模式

   单例模式是设计模式之一,单例模式要求代码中的某个类,只能有一个实例,不能有多个。这种单例模式在开发中非常常见,例如 JDBC 中的 DataSource 这样的对象等等。

   单例模式具体实现方式分为:饿汉模式 和 懒汉模式

  • 饿汉模式:中午吃完饭,用了 3 个碗,就立即把这 3 个碗洗了。(着急)
  • 懒汉模式:中午吃完饭,用了 3 个碗,先不洗。到了晚上吃饭时候,只需要 2 个碗,然后就只洗 2 个碗。(懒汉)(计算机中更加高效)

   饿汉的单例模式是比较着急的去进行创建实例

   懒汉的单例模式是不太着急的去创建实例,只是在用的时候才真正创建

1. 饿汉模式

   类加载阶段,就会直接创建实例。(static)

    static 修饰的成员更准确的来说,应该叫做“类成员” -> “类属性/类方法”。一个java程序中,一个类对象只存在一份(JVM保证的),进一步就保证了类的 static 成员也是只有一份。

   不加static 修饰的成员,叫做“实例成员” -> “实例属性/实例方法”。

class Singleton {
    //1. 使用 static 创建一个实例,并且立即进行实例化
    //这个 instance 对应的实例,就是该类的唯一实例
    private static Singleton instance = new Singleton();
    //2. 为了防止程序员在其他地方不小心 new 了这个 Singleton,所以可以把构造方法设置为 private
    private Singleton() {}
    //3. 提供一个方法,让外面能够拿到这个唯一实例 instance
    public static Singleton getInstance() {
        return instance;
    }
}

饿汉模式要注意:

  • ① 提前创建出来一个实例,并进行初始化
  • ② 将构造方法私有化
  • ③ 提供一个方法,来获取这个实例

2. 懒汉模式

   类加载的时候不创建实例,第一次用的时候才创建实例。

根据基本思路:先大概写出来代码(不是最终代码)

  • ① 提前创建出来一个实例,并进行初始化
  • ② 将构造方法私有化
  • ③ 提供一个方法,来获取这个实例
class Singleton2 {
    //1. 不立即就初始化实例
    private static Singleton2 instance = null;
    //2. 把构造方法设为 private
    private Singleton2() {}
    //3. 提供一个方法来获取上述单例的实例
    public static Singleton2 getInstance() {
        if (instance == null) {
            instance = new Singleton2();
        }
        return instance;
    }
}

 上述代码是线程不安全的,我们需要考虑:

   1. 在 getInstance 方法中,用到了 if 条件判断语句,结构内部又进行了修改操作。因此可以看做是读操作和修改(写)操作。在多线程中,写操作不是原子性的(包含了 3 条操作),因此会出现线程不安全的情况。所以我们需要用 加锁 synchronized 关键字将这两条语句进行捆绑,保证原子性。(锁对象:类对象

   2. 在 getInstance 方法中, if 条件判断语句中的判断条件需要一直连续的读取 instance 的值,这就会导致编译器的优化。所以我们需要用 volatile 关键字来禁止编译器对 instance 值读取的优化,保证内存可见性。 

   3. 考虑两种情况:

  (1)当instance == null,即还没有被初始化时,频繁获取锁再判断是必要的。

  (2)当 instance 已经初始化过了,再使用 getInstance 方法获取实例时,直接返回 instance 的值即可。而如果像上述代码,还要频繁的获取锁,加锁,进行读操作,会大大降低运行效率。所以我们需要在 synchronized 再进行一次判断(instance == null),即双重 if 判定避免无脑加锁现象,进而提高运行效率。

  •    如果为 null,说明是第一次,进行加锁判断、初始化。
  •    如果不为null,说明已经初始化过,直接返回 instance。(不进入外层 if 内部,即不进行获取锁的操作)

正确的懒汉模式: 

class Singleton2 {
    //1. 不立即就初始化实例
    private static volatile Singleton2 instance = null;
    //2. 把构造方法设为 private
    private Singleton2() {}
    //3. 提供一个方法来获取上述单例的实例
    public static Singleton2 getInstance() {
        if (instance == null) {
            synchronized (Singleton2.class) {
                if (instance == null) {
                    instance = new Singleton2();
                }
            }
        }
        return instance;
    }
}

懒汉模式要注意:

  • ① 正确的位置加锁
  • ② volatile
  • ③ 双重 if 判定

 

   

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值