单例模式的七种写法,你都知道吗?

private static Singleton_2 instance;

private Singleton_2() {

}

public static Singleton_2 getInstance() {

if (instance == null) {

instance = new Singleton_2();

}

return instance;

}

}

懒汉式 是什么呢?只有用到的时候才会加载,这就实现了我们心心念的懒加载。

但是!

它又引入了新的问题?什么问题呢?线程安全问题。

懒汉式线程不安全

图片也很清楚,多线程的情况下,可能存在这样的问题:

一个线程判断instance==null,开始初始化对象;

还没来得及初始化对象时候,另一个线程访问,判断instance==null,也创建对象。

最后的结果,就是实例化了两个Singleton对象。

这不符合我们单例的要求啊?怎么办呢?

3、懒汉式(加锁)

===========================================================================

public class Singleton_3 {

private static Singleton_3 instance;

private Singleton_3() {

}

public synchronized static Singleton_3 getInstance() {

if (instance == null) {

instance = new Singleton_3();

}

return instance;

}

}

最直接的办法,直接上锁呗!

但是这种把锁直接方法上的办法,所有的访问都需要获取锁,导致了资源的浪费。

那怎么办呢?

4、懒汉式(双重校验锁)⭐

===============================================================================

public class Singleton_4 {

//volatile修饰,防止指令重排

private static volatile Singleton_4 instance;

private Singleton_4() {

}

public static Singleton_4 getInstance() {

//第一重校验,检查实例是否存在

if (instance == null) {

//同步块

synchronized (Singleton_4.class) {

//第二重校验,检查实例是否存在,如果不存在才真正创建实例

if (instance == null) {

instance = new Singleton_4();

}

}

}

return instance;

}

}

这是比较推荐的一种,双重校验锁。

它的进步在哪里呢?

我们把synchronized加在了方法的内部,一般的访问是不加锁的,只有在instance==null的时候才加锁。

同时我们来看一下一些关键问题。

  • 首先我们看第一个问题,为什么要双重校验?

大家想一下,如果不双重校验。

如果两个线程一起调用getInstance方法,并且都通过了第一次的判断instance==null,那么第一个线程获取了锁,然后实例化了instance,然后释放了锁,然后第二个线程得到了线程,然后马上也实例化了instance。这就不符合我们的单例要求了。

接着我们来看第二个问题,为什么要用volatile 修饰 instance?

我们可能知道答案是防止指令重排。

那这个重排指的是哪?指的是instance = new Singleton(),我们感觉是一步操作的实例化对象,实际上对于JVM指令,是分为三步的:

  1. 分配内存空间

  2. 初始化对象

  3. 将对象指向刚分配的内存空间

有些编译器为为了性能优化,可能会把第二步和第三步进行重排序,顺序就成了:

  1. 分配内存空间

  2. 将对象指向刚分配的内存空间

  3. 初始化对象

指令重排

所以呢,如果不使用volatile防止指令重排可能会发生什么情况呢?

访问到未初始化对象

在这种情况下,T7时刻线程B对instance的访问,访问的是一个初始化未完成的对象。

所以需要在instance前加入关键字volatile

  • 使用了volatile关键字后,可以保证有序性,指令重排序被禁止;

  • volatile还可以保证可见性,Java内存模型会确保所有线程看到的变量值是一致的。

5、单例模式(静态内部类)

===============================================================================

public class Singleton_5 {

private Singleton_5() {

}

private static class InnerSingleton {

private static final Singleton_5 instance = new Singleton_5();

}

public static Singleton_5 getInstance() {

return InnerSingleton.instance;

}

}

静态内部类是更进一步的写法,不仅能实现懒加载、线程安全,而且JVM还保持了指令优化的能力。

Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会加载静态内部类InnerSingleton类,从而完成Singleton的实例化。

类的静态属性只会在第一次加载类的时候初始化,同时类加载的过程又是线程互斥的,JVM帮助我们保证了线程安全。

6、单例模式(CAS)

=============================================================================

public class Singleton_6 {

private static final AtomicReference<Singleton_6> INSTANCE = new AtomicReference<Singleton_6>();

private Singleton_6() {

}

public static final Singleton_6 getInstance() {

//等待

while (true) {

Singleton_6 instance = INSTANCE.get();

if (null == instance) {

INSTANCE.compareAndSet(null, new Singleton_6());

}

return INSTANCE.get();

}

}

}

这种CAS式的单例模式算是懒汉式直接加锁的一个变种,sychronized是一种悲观锁,而CAS是乐观锁,相比较,更轻量级。

当然,这种写法也比较罕见,CAS存在忙等的问题,可能会造成CPU资源的浪费。

7、单例模式(枚举)

============================================================================

public enum Singleton_7 {

//定义一个枚举,代表了Singleton的一个实例

INSTANCE;

public void anyMethod(){

System.out.println(“do any thing”);

}

}

调用方式:

@Test

void anyMethod() {

Singleton_7.INSTANCE.anyMethod();

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

结尾

查漏补缺:Java岗 千+道面试题Java基础+全家桶+容器+反射+异常等

这不止是一份面试清单,更是一种”被期望的责任“,因为有无数个待面试者,希望从这篇文章中,找出通往期望公司的”钥匙“,所以上面每道选题都是结合我自身的经验于千万个面试题中经过艰辛的两周,一个题一个题筛选出来再次对好答案和格式做出来的,面试的答案也是再三斟酌,深怕误人子弟是小,影响他人仕途才是大过,也希望您能把这篇文章分享给更多的朋友,让他帮助更多的人,帮助他人,快乐自己,最后,感谢您的阅读。

由于细节内容实在太多啦,在这里我花了两周的时间把这些答案整理成一份文档了,在这里只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

结尾

[外链图片转存中…(img-h4qF6nBr-1713555005729)]

这不止是一份面试清单,更是一种”被期望的责任“,因为有无数个待面试者,希望从这篇文章中,找出通往期望公司的”钥匙“,所以上面每道选题都是结合我自身的经验于千万个面试题中经过艰辛的两周,一个题一个题筛选出来再次对好答案和格式做出来的,面试的答案也是再三斟酌,深怕误人子弟是小,影响他人仕途才是大过,也希望您能把这篇文章分享给更多的朋友,让他帮助更多的人,帮助他人,快乐自己,最后,感谢您的阅读。

由于细节内容实在太多啦,在这里我花了两周的时间把这些答案整理成一份文档了,在这里只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值