对单例模式的饿汉式、懒汉式的思考

2024-03-03 02:04

本文主要是介绍对单例模式的饿汉式、懒汉式的思考,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 1 什么是单例模式?
    • 1.1 什么是饿汉式?
    • 1.2 什么是懒汉式?
  • 2 我对饿汉式的思考
  • 3 懒汉式
    • 3.1 解决懒汉式的线程安全问题
      • 3.1.1 加锁:synchronized(synchronized修饰静态方法)
      • 3.1.2 对“3.1.1”性能的改进

1 什么是单例模式?

  • 单例模式是指一个类在JVM中只有一个实例。

1.1 什么是饿汉式?

  • 在类加载的时候就创建好了实例。

1.2 什么是懒汉式?

  • 创建实例延迟到使用该实例前。

2 我对饿汉式的思考

  • 示例
public class LearnSingleton {private static LearnSingleton instance = new LearnSingleton();private LearnSingleton() {}public static LearnSingleton getInstance() {return instance;}
}
  • 当我们调用LearnSingleton.getInstance()时,会触发LearnSingleton的加载。在类加载的准备阶段,创建好了静态变量表(此时instance对应的slot槽还为null),等到了初始化阶段,开始执行clinit方法,会创建LearnSingleton的示例(执行了new LearnSingleton()语句)。类加载后,执行getInstance()方法,将单例返回给用户。
    • 这么一看,上述写法不也是懒汉式吗?在使用时才创建实例啊。(虽然,这个实例也是在类加载时就创建好的)
  • 要想明白这到底是懒汉式,还是饿汉式,关键在于:
public class LearnSingleton {private static LearnSingleton instance = new LearnSingleton();private LearnSingleton() {}public static LearnSingleton getInstance() {return instance;}public static int add(int x, int y) {return x + y;}
}
  • 当用户调用LearnSingleton.add(1, 2)时,在类加载过程中,就已经创建好了单例,但并未使用。因此,这不符合在需要时创建单例的定义。从这个例子,就能想明白了,这种写法是饿汉式。
  • 彻底的饿汉式:
public class LearnSingleton {private static final LearnSingleton instance = new LearnSingleton();private LearnSingleton() {}public static LearnSingleton getInstance() {return instance;}
}
  • 只要这个类加载了,由于instance是常量,因此在类加载的准备阶段就创建好了单例。这是彻底的饿汉式。可谓“饿疯了”😃。
  • 饿汉式,是在类加载阶段完成实例的创建,由JVM保证了线程安全。

3 懒汉式

  • 在调用时才创建对象,示例:
public class LearnLazySingleton {private static LearnLazySingleton instance;private LearnLazySingleton() {}// 存在线程安全问题。假设两个线程同时调用该方法,那么可能导致创建2个LearnLazySingleton对象。public static LearnLazySingleton getInstance() {if (instance == null) {instance = new LearnLazySingleton();}return instance;}
}

3.1 解决懒汉式的线程安全问题

3.1.1 加锁:synchronized(synchronized修饰静态方法)

public class LearnLazySingleton {private static LearnLazySingleton instance;private LearnLazySingleton() {}public synchronized static LearnLazySingleton getInstance() {if (instance == null) {instance = new LearnLazySingleton();}return instance;}public synchronized static int add(int x, int y) {return x + y;}public static int sub(int x, int y) {return x - y;}
}
  • 当线程A先调用getInstance()方法的同时, 另一个线程B尝试访问add()方法,线程B会因为没有LearnLazySingleton的class对象的锁而等待。如果类中的其他方法不是synchronized的,它们就不会被锁定,即线程C调用sub方法()就不会等待。
    • 本质是因为,在调用同步方法前,只有获取锁,才能进入临界区。而如果不是临界区,那就不会受影响。

3.1.2 对“3.1.1”性能的改进

  • 上面的写法,有个非常难受的地方,例如,线程A已经调用getInstance()方法,创建好了单例。但线程B为了获取单例,也不得不调用getInstance()方法(唯一获取实例的入口),这时候就可能和线程C撞车,因为只要线程C调用add方法(),就可能让线程B获取单例时发生阻塞。
  • 改进:
public class LearnLazySingleton {private static LearnLazySingleton instance;private LearnLazySingleton() {}public static LearnLazySingleton getInstance() {if (instance == null) {synchronized (LearnLazySingleton.class) {// 这里一旦要在判断下,否则,线程A和线程B在这里排队进入临界区,会导致创建多个实例。if (instance == null) {instance = new LearnLazySingleton();}}}return instance;}public synchronized static int add(int x, int y) {return x + y;}
}
  • 在创建单例前,仍然存在线程A和线程B争抢着执行“instance = new LearnLazySingleton();”,也会影响线程C去调用add()方法。但一旦实例创建好后,instance不为null,线程们调用getInstance()方法就不会阻塞了。

这篇关于对单例模式的饿汉式、懒汉式的思考的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/768113

相关文章

关于rpc长连接与短连接的思考记录

《关于rpc长连接与短连接的思考记录》文章总结了RPC项目中长连接和短连接的处理方式,包括RPC和HTTP的长连接与短连接的区别、TCP的保活机制、客户端与服务器的连接模式及其利弊分析,文章强调了在实... 目录rpc项目中的长连接与短连接的思考什么是rpc项目中的长连接和短连接与tcp和http的长连接短

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

模版方法模式template method

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/template-method 超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。 上层接口有默认实现的方法和子类需要自己实现的方法

【iOS】MVC模式

MVC模式 MVC模式MVC模式demo MVC模式 MVC模式全称为model(模型)view(视图)controller(控制器),他分为三个不同的层分别负责不同的职责。 View:该层用于存放视图,该层中我们可以对页面及控件进行布局。Model:模型一般都拥有很好的可复用性,在该层中,我们可以统一管理一些数据。Controlller:该层充当一个CPU的功能,即该应用程序

迭代器模式iterator

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/iterator 不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素

《x86汇编语言:从实模式到保护模式》视频来了

《x86汇编语言:从实模式到保护模式》视频来了 很多朋友留言,说我的专栏《x86汇编语言:从实模式到保护模式》写得很详细,还有的朋友希望我能写得更细,最好是覆盖全书的所有章节。 毕竟我不是作者,只有作者的解读才是最权威的。 当初我学习这本书的时候,只能靠自己摸索,网上搜不到什么好资源。 如果你正在学这本书或者汇编语言,那你有福气了。 本书作者李忠老师,以此书为蓝本,录制了全套视频。 试

利用命令模式构建高效的手游后端架构

在现代手游开发中,后端架构的设计对于支持高并发、快速迭代和复杂游戏逻辑至关重要。命令模式作为一种行为设计模式,可以有效地解耦请求的发起者与接收者,提升系统的可维护性和扩展性。本文将深入探讨如何利用命令模式构建一个强大且灵活的手游后端架构。 1. 命令模式的概念与优势 命令模式通过将请求封装为对象,使得请求的发起者和接收者之间的耦合度降低。这种模式的主要优势包括: 解耦请求发起者与处理者

springboot实战学习(1)(开发模式与环境)

目录 一、实战学习的引言 (1)前后端的大致学习模块 (2)后端 (3)前端 二、开发模式 一、实战学习的引言 (1)前后端的大致学习模块 (2)后端 Validation:做参数校验Mybatis:做数据库的操作Redis:做缓存Junit:单元测试项目部署:springboot项目部署相关的知识 (3)前端 Vite:Vue项目的脚手架Router:路由Pina:状态管理Eleme

状态模式state

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/state 在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。 在状态模式中,player.getState()获取的是player的当前状态,通常是一个实现了状态接口的对象。 onPlay()是状态模式中定义的一个方法,不同状态下(例如“正在播放”、“暂停