单例模式(多种写法)
什么是单例模式?
单例模式就是保证一个类只有一个对象的实例,实现这种功能的方式就叫单例模式。
如何实现单例模式
因为保证一个类只能有一个实例,不能多次实例化,不能允许用户new对象,所以需要将构造方法私有化,通过提供类的方法来让外部获取对象实例
单例模式主要存在两种方式实现:饿汉式和懒汉式
饿汉式
饿汉式即不管你需不要这个对象,先创建好,等你用的时候直接拿来用就行。
饿汉式代码实现:
饿汉式是线程安全的。
懒汉式
懒汉式即需要用到该实例的时候再去创建对象。
该懒汉式代码在单线程下执行是没有问题的,但是在多线程下执行的话可能会因为并发问题导致实例化多次。因此可以对懒汉式加锁来控制类只允许被实例化一次。
懒汉式(加锁版)
通过加锁保证了单例模式的安全性。但是锁的粒度太大,会严重影响性能。因此可以采用DCL双重检锁机制。
懒汉式(DCL版)
这样的话就是只有当对象未创建的时候才请求加锁,对象创建以后都不会再去获取锁加锁。这样就可以提高程序效率,并保证实例只有一个。但这段代码依然存在问题,因为JVM是存在指令重排的,所以在多线程下不一定是线程安全的。原因是当某一个线程第一次检测的时候,读取到instance不为空的话,instance的引用对象可能没有完成实例化。因为 instance = new Lazy();可以分为以下三步进行完成:
- memory = allocate(); // 1、分配对象内存空间
- instance(memory); // 2、初始化对象
- instance = memory; // 3、设置instance指向刚刚分配的内存地址,此时instance != null
因为2和3是不存在依赖关系的,所以可能会发生指令重排,在单线程下是完全没有问题的,但是多线程下就需要禁止重排。 - memory = allocate(); // 1、分配对象内存空间
- instance = memory; // 3、设置instance指向刚刚分配的内存地址,此时instance != null
- instance(memory); // 2、初始化对象
当我们执行到重排后的步骤2,试图获取instance的时候,会得到null,因为对象的初始化还没有完成,而是在重排后的步骤3才完成,因此执行单例模式的代码时候,就会重新在创建一个instance实例。所以当一条线程访问instance不为null时,由于instance实例未必已初始化完成,这就造成了线程安全的问题。指令重排只会保证串行语义的执行一致性(单线程),但并不会关系多线程间的语义一致性
解决以上问题便可以使用volatile关键字,来禁止指令重排,保证单例模式的线程安全性。
最终代码为:
懒汉式(最终版)
__EOF__

本文链接:https://www.cnblogs.com/youngerwb/p/16154386.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)