单例模式底层避免线程安全问题的设计思想

单例模式(Singleton Pattern)是一种常用的软件设计模式,它确保一个类只有一个实例,并提供一个全局访问点。这种模式在需要全局状态或者希望整个应用程序中某个对象只有一个实例时非常有用。

单例模式的关键特点包括:

  1. 唯一实例:单例类通过私有构造函数和静态实例变量确保只有一个实例被创建。
  2. 全局访问点:单例类提供一个公共的静态方法,允许其他对象获取到这个唯一实例的引用。
  3. 延迟初始化:单例实例可以在第一次使用时创建,也可以在类加载时立即创建。延迟初始化有助于节省资源,因为它允许系统在需要时才创建实例。
  4. 线程安全:在多线程环境中,单例模式需要确保线程安全,避免多个线程同时创建多个实例。

单例模式的几种实现方式:

1.饿汉模式

public class EagerSingleton {
    private static final EagerSingleton instance = new EagerSingleton();

    private EagerSingleton() {}

    public static EagerSingleton getInstance() {
        return instance;
    }
}

这种实现方式会在类加载的过程中就创建唯一实例,并不存在线程安全问题。但是,如果实例从未被使用,这种方式会造成资源浪费,而且当我们的对象比较大时,这种方式会在一定程度上影响我们类的启动速度。

2.懒汉模式

public class LazySingleton {
    private static volatile LazySingleton instance;

    private LazySingleton() {}

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

这种模式为什么要使用双重检测锁,接下来我们来看看原因:

首先在只加一层判断,不加锁时,很明显也存在线程安全的问题,如图

出现了线程安全问题,我们很容易想到使用synz上锁,对我们的方法上锁,确实这样可以避免了我们的线程安全问题,但是锁的力度太大了,我们平常在开发过程中其实还会有其他的业务代码,而这些代码并不是锁要竞争的资源,所以我们应该锁临界资源=多线程竞争的资源。

好,那我们对竞争资源上锁,咦,这是我们有出现线程安全问题了,如图,我们线程1和线程2同时通过了判断1,此时假设线程1抢到了锁,进行实例化,结束流程,释放锁资源,线程2拿到锁,同样进行下面的流程,进行实例化,这些尴尬了,回到了最开始的问题,那怎么办呢?

其实只要在加一层判断就完事了!我们可以在脑子里过一遍流程,这样一来既可以解决了线程安全的问题也可以比避免我们锁的力度太大的问题。

这里在介绍一下volatile,在我们的源码中其实加上了volatile关键词,目的是避免指令重排,保证我们变量的可见性。

什么是指令重排?

我们的单例模式编译成指令后,uniqueInstance = new Singleton(); 这段代码其实是分为三步执行:

  1. 为 uniqueInstance 分配内存空间
  2. 初始化 uniqueInstance
  3. 将 uniqueInstance 指向分配的内存地址

我们JVM的底层可能为了性能,这个顺序可能会被打乱, 执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。

3.静态内部类

public class StaticInnerClassSingleton {
    private StaticInnerClassSingleton() {}

    public static StaticInnerClassSingleton getInstance() {
        return Holder.INSTANCE;
    }

    private static class Holder {
        private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
    }
}

这种实现利用了Java的类加载机制来保证实例的唯一性和线程安全性,无需额外的同步机制。

  • 26
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值