【编码魔法师系列_构建型2.2】单例模式「懒汉式」(Singleton Pattern)

本文主要是介绍【编码魔法师系列_构建型2.2】单例模式「懒汉式」(Singleton Pattern),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

👉直达编码魔法师系列其他文章👈

学会设计模式,你就可以像拥有魔法一样,在开发过程中解决一些复杂的问题。设计模式是由经验丰富的开发者们(GoF)凝聚出来的最佳实践,可以提高代码的可读性、可维护性和可重用性,从而让我们的开发效率更高。通过不断的练习和实践,掌握其中的奥妙,选择合适的设计模式,能为我们的项目增加一丝神奇的魔力。

文章目录

  • 实例:
  • 目的:
  • 适用场景:
  • 优点:
  • 弊端:
  • 类图:
  • 框架用到的地方:
  • Coding:
    • 线程不安全
      • 将getInstance()方法进行改造
      • 测试:
      • 测试结果:
    • 双检锁方式——线程安全
      • 改造getinstance()方法
      • 测试
      • 测试结果
    • 利用枚举
    • 将饿汉和懒汉结合:
      • 测试
      • 测试结果:

实例:

模拟多线程下单例模式(懒汉式)初始化对象

目的:

使得一个全局使用的类不会被频繁地创建与销毁。

适用场景:

1、需要频繁地进行创建和销毁的对象
2、创建对象耗时或耗费资源过多,但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)。

优点:

在类加载时不初始化,使用时才创建,因此类加载速度快,节约资源

弊端:

运行时获取对象的速度慢。在多线程的场景下,为了实现线程安全,要付出额外代价

类图:

在这里插入图片描述

框架用到的地方:

java.lang.Runtime;spring依赖注入

Coding:

线程不安全

/*** 线程不安全*/
public class LazySingleton {private static LazySingleton instance = null;private LazySingleton() {}public static LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}public void showMsg() {System.out.println("This is LazySingleton!");}
}

这种方式,不能保证线程安全,接下来我们模拟一个多线程场景:

将getInstance()方法进行改造

public static LazySingleton getInstance() {if (instance == null) {try {Thread.sleep(1000L);} catch (InterruptedException e) {throw new RuntimeException(e);}instance = new LazySingleton();}return instance;
}

测试:

@Test
public void Test1() {new Thread(new Runnable() {@Overridepublic void run() {System.out.println(LazySingleton.getInstance());}}).start();System.out.println(LazySingleton.getInstance());
}

测试结果:

LazySingleton@48cf768c
LazySingleton@47382356

上述代码之所以说它“线程不安全”是因为:在多个线程同时调用getInstance()方法的时候,大概率会同时创建多个实例,所以此时我们不得不加上“synchronized”来避免这个问题,但是我们可以很快的发现:假设多个线程都在竞争锁,又会严重影响并发性能,所以我们只需要在竞争锁之前先判断一次实例是否此时为null,就可以减少不必要的抢锁动作,至此,我们就完成了“双检锁方式”,这是一种比较好的单例实现模式。

双检锁方式——线程安全

/*** 双检锁方式* 线程安全*/
public class LazySingletonV2 {private static LazySingletonV2 instance = null;private LazySingletonV2() {}public static LazySingletonV2 getInstance() {//如果instance不为null,无需抢锁,返回instance即可if (instance == null) {//避免多线程调用时,创建多个instancesynchronized (LazySingletonV2.class) {//抢到锁的线程判断是否为nullif (instance == null) {instance = new LazySingletonV2();}}}return instance;}public void showMsg() {System.out.println("This is LazySingletonV2!");}
}

synchronized:等一个人执行完毕,才会继续执行
模仿上面的实验,我们再做一次:

改造getinstance()方法

public static LazySingletonV2 getInstance() {//如果instance不为null,无需抢锁,返回instance即可if (instance == null) {//避免多线程调用时,创建多个instance≈synchronized (LazySingletonV2.class) {try {//模拟创建对象之前做的准备工作Thread.sleep(3000L);} catch (InterruptedException e) {throw new RuntimeException(e);}//抢到锁的线程判断是否为nullif (instance == null) {instance = new LazySingletonV2();}}}return instance;
}

测试

@Test
public void Test2() {new Thread(new Runnable() {@Overridepublic void run() {System.out.println(LazySingletonV2.getInstance());}}).start();System.out.println(LazySingletonV2.getInstance());
}

测试结果

LazySingletonV2@5a420d92
LazySingletonV2@5a420d92

当然我们也可以直接利用Java内置的“枚举”来实现单例模式:因为枚举类型是线程安全的,并且只会装载一次。

利用枚举

/*** 利用枚举*/
public enum LazySingletonV3 {INSTANCE;private String Msg = "This is LazySingletonV3!";public String getMsg() {return Msg;}public void setMsg(String msg) {Msg = msg;}
}

将饿汉和懒汉结合:

/*** 饿汉,懒汉结合*/
public class LazySingletonV4 {public static class InnerHolder{private static LazySingletonV4 instance = new LazySingletonV4();}private LazySingletonV4() {System.out.println("LazySingletonV4"+"create");}public static LazySingletonV4 getInstance() {try {Thread.sleep(1000L);} catch (InterruptedException e) {throw new RuntimeException(e);}return InnerHolder.instance;}public void showMsg() {System.out.println("This is LazySingleton!");}
}

测试

@Test
public void Test4() {new Thread(new Runnable() {@Overridepublic void run() {System.out.println(LazySingletonV4.getInstance());}}).start();System.out.println(LazySingletonV4.getInstance());
}

测试结果:

LazySingletonV4  getInstance
LazySingletonV4  getInstance
LazySingletonV4  create
LazySingletonV4@48cf768c
LazySingletonV4@48cf768c

从打印语句可以看出:输出了两次getInstance之后才输出了create,所以只有使用到这个内部类的时候才会被创建
且由java虚拟机创建,是单线程执行,保证了线程安全
这种方式结合了懒汉模式和饿汉模式的优点:高效解决了懒汉模式的线程不安全的问题,同时也不会像饿汉模式一样浪费资源。

👉直达编码魔法师系列其他文章👈

文章后期会持续优化,如果觉得小名的文章帮助到了您,请关注小名,支持一下小名😄,给小名的文章点赞👍、评论✍、收藏🤞谢谢大家啦~♥♥♥
编码魔法师系列文章,会收录在小名的【设计模式】专栏中,希望大家可以持续关注🎉

这篇关于【编码魔法师系列_构建型2.2】单例模式「懒汉式」(Singleton Pattern)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Redis Cluster模式配置

《RedisCluster模式配置》:本文主要介绍RedisCluster模式配置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录分片 一、分片的本质与核心价值二、分片实现方案对比 ‌三、分片算法详解1. ‌范围分片(顺序分片)‌2. ‌哈希分片3. ‌虚

基于Python构建一个高效词汇表

《基于Python构建一个高效词汇表》在自然语言处理(NLP)领域,构建高效的词汇表是文本预处理的关键步骤,本文将解析一个使用Python实现的n-gram词频统计工具,感兴趣的可以了解下... 目录一、项目背景与目标1.1 技术需求1.2 核心技术栈二、核心代码解析2.1 数据处理函数2.2 数据处理流程

Python FastMCP构建MCP服务端与客户端的详细步骤

《PythonFastMCP构建MCP服务端与客户端的详细步骤》MCP(Multi-ClientProtocol)是一种用于构建可扩展服务的通信协议框架,本文将使用FastMCP搭建一个支持St... 目录简介环境准备服务端实现(server.py)客户端实现(client.py)运行效果扩展方向常见问题结

详解如何使用Python构建从数据到文档的自动化工作流

《详解如何使用Python构建从数据到文档的自动化工作流》这篇文章将通过真实工作场景拆解,为大家展示如何用Python构建自动化工作流,让工具代替人力完成这些数字苦力活,感兴趣的小伙伴可以跟随小编一起... 目录一、Excel处理:从数据搬运工到智能分析师二、PDF处理:文档工厂的智能生产线三、邮件自动化:

RabbitMQ工作模式中的RPC通信模式详解

《RabbitMQ工作模式中的RPC通信模式详解》在RabbitMQ中,RPC模式通过消息队列实现远程调用功能,这篇文章给大家介绍RabbitMQ工作模式之RPC通信模式,感兴趣的朋友一起看看吧... 目录RPC通信模式概述工作流程代码案例引入依赖常量类编写客户端代码编写服务端代码RPC通信模式概述在R

详解如何使用Python从零开始构建文本统计模型

《详解如何使用Python从零开始构建文本统计模型》在自然语言处理领域,词汇表构建是文本预处理的关键环节,本文通过Python代码实践,演示如何从原始文本中提取多尺度特征,并通过动态调整机制构建更精确... 目录一、项目背景与核心思想二、核心代码解析1. 数据加载与预处理2. 多尺度字符统计3. 统计结果可

一文教你Java如何快速构建项目骨架

《一文教你Java如何快速构建项目骨架》在Java项目开发过程中,构建项目骨架是一项繁琐但又基础重要的工作,Java领域有许多代码生成工具可以帮助我们快速完成这一任务,下面就跟随小编一起来了解下... 目录一、代码生成工具概述常用 Java 代码生成工具简介代码生成工具的优势二、使用 MyBATis Gen

SQL Server身份验证模式步骤和示例代码

《SQLServer身份验证模式步骤和示例代码》SQLServer是一个广泛使用的关系数据库管理系统,通常使用两种身份验证模式:Windows身份验证和SQLServer身份验证,本文将详细介绍身份... 目录身份验证方式的概念更改身份验证方式的步骤方法一:使用SQL Server Management S

Python使用Reflex构建现代Web应用的完全指南

《Python使用Reflex构建现代Web应用的完全指南》这篇文章为大家深入介绍了Reflex框架的设计理念,技术特性,项目结构,核心API,实际开发流程以及与其他框架的对比和部署建议,感兴趣的小伙... 目录什么是 ReFlex?为什么选择 Reflex?安装与环境配置构建你的第一个应用核心概念解析组件

基于Go语言实现Base62编码的三种方式以及对比分析

《基于Go语言实现Base62编码的三种方式以及对比分析》Base62编码是一种在字符编码中使用62个字符的编码方式,在计算机科学中,,Go语言是一种静态类型、编译型语言,它由Google开发并开源,... 目录一、标准库现状与解决方案1. 标准库对比表2. 解决方案完整实现代码(含边界处理)二、关键实现细