操作系统——总结单例模式的写法(保证线程安全)

目录

一、单例模式

1、饿汉模式

2、懒汉模式

(1)、懒汉模式

(2)、通过synchronized加锁 

(3)、通过双重校验锁判定和volatile关键字


一、单例模式

单例模式是指一个类在进程中只有唯一的一个实例。

单例模式可以被分为饿汉和懒汉两个模式,二者相比饿汉模式是急迫的,而懒汉模式则是从容的。

例:

打开一个硬盘上的文件,读取文件的内容并显示出来。

  • 饿汉模式就是把文件的所有内容都读取到内存中并显示出来。
  • 懒汉模式则是只读取文件的一小部分来填充给当前屏幕页面,如果用户翻页,再读取其他文件内容。

二者相比,前者花费时间更长且可能会内存不够,而后者可以快速打开。但是二者线程安全却是相反的,饿汉模式是天然的线程安全,它只是读数据不修改数据懒汉模式则既读数据又修改数据,是不安全的,多线程下可能无法保证创建对象的唯一性。

1、饿汉模式

饿汉模式就是在类加载时创建对象,这种方式是线程安全的。

class Singleton {
    // 唯一实例的本体
    private static Singleton instance = new Singleton();
    // 获取到实例的方法
    public static Singleton getInstance() {
        return instance;
    }
    // 禁止外部 new 实例
    private Singleton() { }
}

2、懒汉模式

(1)、懒汉模式

懒汉模式是在类加载时不创建对象,而是在第一次使用时才创建对象。

class SingletonLazy {
    private static SingletonLazy instance = null;

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

    private SingletonLazy() { }
}

懒汉模式的代码在单线程下没有任何问题,但是在多线程环境下则存在安全问题。

当对象还没有被创建时,如果有N个线程都调用getInstance()方法,就可能创建N个对象,因此存在线程安全问题。那要如何解决代码中的线程安全问题呢?下面便是改进方法:

(2)、通过synchronized加锁 

class SingletonLazy {
    private static SingletonLazy instance = null;

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

    private SingletonLazy() { }
}

我们可以通过加锁把if和new操作变为原子性来保证线程安全,但加锁可能涉及到阻塞等待,这是一个比较低效的操作。

虽然使用synchronized加锁能够保证线程安全,但是每次调用该方法时都会产生锁的竞争。然而创建实例只需要创建一次,在创建实例后再调用该方法还需要将锁释放,效率较低。因此我们需要再次改进。

(3)、通过双重校验锁判定和volatile关键字

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() { }
}

1、使用双重if判定,可以减少不必要的加锁操作,降低了锁的竞争

  • 外层的if判定:判定是否要加锁,如果对象已经有了, 就不必加锁了, 此时本身就是线程安全的。
  • 内层的if判断:当前线程释放锁后,其他阻塞的线程就会继续竞争锁,但对象已经创建好了,所以为了避免再次产生锁的竞争降低效率,于是就有了内层判定。

2、使用volatile修饰instance,禁止指令重排序保证后续线程能拿到完整对象

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值