【编码魔法师系列_构建型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

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

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

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

Retrieval-based-Voice-Conversion-WebUI模型构建指南

一、模型介绍 Retrieval-based-Voice-Conversion-WebUI(简称 RVC)模型是一个基于 VITS(Variational Inference with adversarial learning for end-to-end Text-to-Speech)的简单易用的语音转换框架。 具有以下特点 简单易用:RVC 模型通过简单易用的网页界面,使得用户无需深入了

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

maven 编译构建可以执行的jar包

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」👈,「stormsha的知识库」👈持续学习,不断总结,共同进步,为了踏实,做好当下事儿~ 专栏导航 Python系列: Python面试题合集,剑指大厂Git系列: Git操作技巧GO

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0

C++ | Leetcode C++题解之第393题UTF-8编码验证

题目: 题解: class Solution {public:static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num &

嵌入式Openharmony系统构建与启动详解

大家好,今天主要给大家分享一下,如何构建Openharmony子系统以及系统的启动过程分解。 第一:OpenHarmony系统构建      首先熟悉一下,构建系统是一种自动化处理工具的集合,通过将源代码文件进行一系列处理,最终生成和用户可以使用的目标文件。这里的目标文件包括静态链接库文件、动态链接库文件、可执行文件、脚本文件、配置文件等。      我们在编写hellowor