当前位置: 首页 > news >正文

线程P5 | 单例模式[线程安全版]~懒汉 + 饿汉

什么是单例模式?

在我们正式讲解单例模式之前,没有了解过的小伙伴可能会有疑问...到底啥叫单例模式??其实单例模式呢,是我们设计模式中的一种,所谓的设计模式,你可以把它理解为一个模板,也就是你在实现某种业务的时候,选择适配的设计模式,根据这个模板来改你对应的业务代码

Java设计模式是解决特定软件设计问题的经典、可复用的方案模板,分为创建型、结构型和行为型三大类,帮助开发者编写更灵活、可维护的代码。

那么我们的单例模式呢,指的是实例对象只会被创建一次这样的设计模式~~

为了实现这样的要求,单例模式中又有两种形式:饿汉模式懒汉模式,接下来我们将会为大家一一介绍这两种模式

饿汉模式

什么是饿汉模式

所谓的饿汉模式,其实指的是实例从代码刚开始运行的时候就已经创建好的模式,那实例就处在一个等着被调用的状态,所以就一直饿着来等待资源~~因此就叫做饿汉模式啦

饿汉模式实现

class Singleton {   //饿汉模式private static Singleton instance = new Singleton();public static Singleton getInstance() {return instance;}private Singleton() {}}
public class Demo19 {public static void main(String[] args) {Singleton t1 = Singleton.getInstance();Singleton t2 = Singleton.getInstance();boolean res = t1 == t2;System.out.println(res);}
}

如上,我们运行结果为true,因为此时它们引用的都是同一个实例~~所以是符合单例模式的

我们可以发现,单例模式的实现,即外部无法创建一个实例是通过将构造方法变为private来实现的,此时的话外部只能通过getInstance()方法来访问内部已经创建好的那个实例

线程安全问题

那么,饿汉模式有没有线程安全问题呢?我们可以发现,饿汉模式中只有读操作,所以是没有线程安全问题的~~可以放心大胆使用

不过饿汉模式还是有缺点的,因为实例一开始就被创建了,一直等着被使用,这是很浪费资源的行为~~

懒汉模式

什么是懒汉模式

所谓的懒汉模式,指的是实例只有在被使用的时候才会开始创建,因此听起来就很懒啦,所以就被叫做懒汉模式,不过这里的"懒"可是褒义词,因为这可以节约资源~~

懒汉模式实现

class SingletonLazy {private static SingletonLazy instance = null;public static SingletonLazy getInstance() {if (instance == null) {             //判断是否有实例创建了instance = new SingletonLazy();}return instance;}private SingletonLazy() {}
}
public class Demo20 {   //懒汉模式public static void main(String[] args) {SingletonLazy t1 = SingletonLazy.getInstance();SingletonLazy t2 = SingletonLazy.getInstance();System.out.println(t1 == t2);}
}

上述我们给出了一个按照描述的懒汉模式,实例一开始是null,只有当被调用的时候才会根据判断实例是否创建过来创建实例

线程安全问题

当我们细看上述代码的时候,其实可以发现,对于instance对象,我们既有读操作,又有写操作,所以事实上,这个代码是存在线程安全问题的

当t1和t2同时调用的时候,有可能会创建两个实例,这就不符合单例模式的要求了;

我们细看可以发现,此时创建实例这个操作并不是原子的,是if + 创建一起的,这也是会引发线程安全问题的原因

因此,为了解决上述问题,我们可以把这个操作加个锁,把它们变成原子的~~

解决原子性问题代码
class SingletonLazy {private static SingletonLazy instance = null;private static Object locker = new Object();public static SingletonLazy getInstance() {synchronized (locker) {                 //使得if和创建实例变成一个整体的原子操作,防止多个线程同时操作时创建多个实例if (instance == null) {             //判断是否有实例创建了instance = new SingletonLazy();}}return instance;}private SingletonLazy() {}
}
public class Demo20 {   //懒汉模式public static void main(String[] args) {SingletonLazy t1 = SingletonLazy.getInstance();SingletonLazy t2 = SingletonLazy.getInstance();System.out.println(t1 == t2);}
}

OK~~我们的原子性问题已经解决啦(●'◡'●)

但是...我们再来看下这个代码,线程安全问题有没有解决完呢?回顾我们P4中提到的引起线程安全问题的原因,此时其实我们是两个线程在对一个对象进行操作,所以是很可能发生指令重排序和内存可见性问题的,因此我们应该给这个对象加上volatile~~

解决指令重排序和内存可见性问题
class SingletonLazy {private static volatile SingletonLazy instance = null;  //volatile防止指令重排序private static Object locker = new Object();public static SingletonLazy getInstance() {synchronized (locker) {                 //使得if和创建实例变成一个整体的原子操作,防止多个线程同时操作时创建多个实例if (instance == null) {             //判断是否有实例创建了instance = new SingletonLazy();}}return instance;}private SingletonLazy() {}
}
public class Demo20 {   //懒汉模式public static void main(String[] args) {SingletonLazy t1 = SingletonLazy.getInstance();SingletonLazy t2 = SingletonLazy.getInstance();System.out.println(t1 == t2);}
}

OK~~~这下这两个问题也解决啦(●'◡'●)

这样看起来,线程好像是没啥安全问题了勒,那有没有啥可以优化的?

经典生活例子

为了使大家更好理解我们的优化策略,在po出代码之前,我们先搞个生活化例子理解一下~~

比如说学校里面的校花突然恢复单身了,那广大单身男青年们听到这个消息之后都很开心呐,不过大家素质都很高,于是在追求校花的时候排起了队,按序追求,你很幸运的抢到了第一个,你开始追求校花之后就相当于追求校花这个操作加锁了,别人就不能进行了,校花觉得你特别好,于是答应和你在一起啦~~[成功创建了实例],但是队伍里面的人此时还不知道,第二个人此时又去追求校花,追求校花这个操作就又加锁了,这个时候她就说我有男朋友啦,第二个人出去是不是就会告诉其它人,校花有男朋友了这件事情,那么实际上,他们就不会再进行追求校花这个操作了,即甚至连竞争锁这个操作都不会去做,因为这是浪费资源的

所以与例子同样的问题,我们这个代码此时每个线程都还会再去竞争一次锁,但如果实例已经存在了,就没有竞争锁的必要了,只有一开始的几个线程在实例被创建好之前就进入创建实例过程会竞争一下锁~~~

解决重复竞争锁问题
class SingletonLazy {private static volatile SingletonLazy instance = null;  //volatile防止指令重排序private static Object locker = new Object();public static SingletonLazy getInstance() {if (instance == null) {                     //后续线程可以防止重复加锁synchronized (locker) {                 //使得if和创建实例变成一个整体的原子操作,防止多个线程同时操作时创建多个实例if (instance == null) {             //判断是否有实例创建了instance = new SingletonLazy();}}}return instance;}private SingletonLazy() {}
}
public class Demo20 {   //懒汉模式public static void main(String[] args) {SingletonLazy t1 = SingletonLazy.getInstance();SingletonLazy t2 = SingletonLazy.getInstance();System.out.println(t1 == t2);}
}

标准懒汉模式实现代码

我们这里总结一下解决完所有问题之后的代码

class SingletonLazy {private static volatile SingletonLazy instance = null;  //volatile防止指令重排序private static Object locker = new Object();public static SingletonLazy getInstance() {if (instance == null) {                     //后续线程可以防止重复加锁synchronized (locker) {                 //使得if和创建实例变成一个整体的原子操作,防止多个线程同时操作时创建多个实例if (instance == null) {             //判断是否有实例创建了instance = new SingletonLazy();}}}return instance;}private SingletonLazy() {}
}
public class Demo20 {   //懒汉模式public static void main(String[] args) {SingletonLazy t1 = SingletonLazy.getInstance();SingletonLazy t2 = SingletonLazy.getInstance();System.out.println(t1 == t2);}
}

❤❤感谢观看~~对你有帮助的话给博主点个大大的赞吧(●'◡'●)谢谢~~~❤❤

http://www.lryc.cn/news/622356.html

相关文章:

  • 【C#补全计划】委托
  • Vue 侦听器(watch 与 watchEffect)全解析2
  • SSH协议的GIT转换
  • pyecharts可视化图表-pie:从入门到精通(进阶篇)
  • 集成电路学习:什么是Image Segmentation图像分割
  • GPT-5 官方前瞻:它将如何重塑你的数字生活?
  • 艾伦·图灵:计算理论与人工智能的奠基人
  • Linux————网络基础
  • 二分算法(模板)
  • 数据结构与算法p4
  • 什么是ai智能?AI的九年飞跃史:从AlphaGo到Agent智能体
  • 项目管理工具
  • 图说据小学常识证伪数学公理——平面公理是将无穷多各异平面误为同一面的“井底蛙”误区
  • LINUX服务运行CPU平均负载率异常高,CPU占用高
  • ollama大模型
  • fpga高速接口汇总整理
  • 让数据可视化更简单:Embedding Atlas使用指南
  • k8s环境使用Operator部署Seaweedfs集群(一)
  • 【反序列化基本介绍】
  • 48Days-Day19 | ISBN号,kotori和迷宫,矩阵最长递增路径
  • Point-LIO技术文档中文翻译解析
  • 文章数据发布到苹果CMS(MacCMS)网站技巧
  • ETH持续上涨推动DEX热潮,交易活跃度飙升的XBIT表现强势出圈
  • 图论Day3学习心得
  • 【机器学习】核心分类及详细介绍
  • 开疆智能ModbusTCP转Ethernet网关连接FBOX串口服务器配置案例
  • 【iOS】多线程原理
  • 昇腾AI自学Day1-- 深度学习基础工具与数学
  • C语言基础08——文件的输入与输出
  • git clone https://gh.llkk.cc/