【JavaEE】 饿汉模式与懒汉模式详解与实现

news2024/11/27 2:46:57

文章目录

  • 🌴单例模式
  • 🍀饿汉模式
  • 🎍懒汉模式
    • 🚩单线程版(线程不安全)
    • 🚩多线程版
    • 🚩多线程版(改进)
  • ⭕总结

🌴单例模式

单例模式是校招中最常考的设计模式之一.

那么啥是设计模式呢?

  • 设计模式好比象棋中的 “棋谱”. 红方当头炮, 黑方马来跳. 针对红方的一些走法, 黑方应招的时候有一些固定的套路. 按照套路来走局势就不会吃亏.

  • 软件开发中也有很多常见的 “问题场景”. 针对这些问题场景, 大佬们总结出了一些固定的套路. 按照这个套路来实现代码, 也不会吃亏.

那么什么是单例模式呢?

  • 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

  • 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

  • 单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供了一个全局访问点来访问该实例。

注意:

  1. 单例类只能有一个实例。

  2. 单例类必须自己创建自己的唯一实例。

  3. 单例类必须给所有其他对象提供这一实例

单例模式具体的实现方式, 又分成 “饿汉” 和 “懒汉” 两种

🍀饿汉模式

饿汉模式,就是它很饿,它的对象早早的就创建好了

这种方式比较常用,但容易产生垃圾对象。

类加载的同时, 创建实例

代码实现如下:

class Singleton {
    //创建新实例,static修饰,只创建一次
    private static Singleton instance = new Singleton();
    //构造方法进行封装,不能创建新实例
    private Singleton() {}
    //唯一实例的获取方法
    public static Singleton getInstance() {
        return instance;
    }
}
  • 优点:没有加锁,执行效率会提高。

  • 缺点:类加载时就初始化,浪费内存。

🎍懒汉模式

懒汉模式,顾名思义就是懒,没有对象需要调用它的时候不去实例化,有人来向它要对象的时候再实例化对象

🚩单线程版(线程不安全)

这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。

因为没有加锁synchronized,所以严格意义上它并不算单例模式

代码实现如下:

class Singleton {
    private static Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        //需要用到时进行创建
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

上面的懒汉模式的实现是线程不安全的.

  • 线程安全问题发生在首次创建实例时. 如果在多个线程中同时调用 getInstance 方法, 就可能导致
    创建出多个实例.

但是一旦实例已经创建好了, 后面再多线程环境调用 getInstance 就不再有线程安全问题了(不再修改instance 了)

🚩多线程版

所以我们对上述代码进行了优化

我们可以加上 synchronized 可以改善这里的线程安全问题.

优化代码如下:

class Singleton {
	private static Singleton instance = null;
	private Singleton() {}
	public synchronized static Singleton getInstance() {
		if (instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}
  • 优点:第一次调用才初始化,避免内存浪费。

  • 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率

该版的懒汉模式还存在一个问题,就是内存可见性的问题,不知道内存可见性的宝子可以去看看博主写的【JavaEE初阶】 volatile关键字 与 wait()方法和notify()方法详解里面对volati关键字部分的讲解

🚩多线程版(改进)

所以我们针对上述多线程版的懒汉模式进行了改进

以下代码在加锁的基础上, 做出了进一步改动:

  • 使用双重 if 判定, 降低锁竞争的频率.

  • 给 instance 加上了 volatile.

代码实现如下:

class Singleton {
    private static volatile Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        //需要用到时进行创建
        if(instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

那么双重 if 判定 / volatile是怎么达到这样的效果的呢?

  • 加锁 / 解锁是一件开销比较高的事情. 而懒汉模式的线程不安全只是发生在首次创建实例的时候.

  • 因此后续使用的时候, 不必再进行加锁了.

  • 外层的 if 就是判定下看当前是否已经把 instance 实例创建出来了.

  • 同时为了避免 “内存可见性” 导致读取的 instance 出现偏差, 于是补充上 volatile .

  • 当多线程首次调用 getInstance, 大家可能都发现 instance 为 null, 于是又继续往下执行来竞争锁,其中竞争成功的线程, 再完成创建实例的操作.

  • 当这个实例创建完了之后, 其他竞争到锁的线程就被里层 if 挡住了. 也就不会继续创建其他实例.

比如一下实例:

  1. 有三个线程, 开始执行 getInstance , 通过外层的 if (instance == null) 知道了实例还没有创建的消息. 于是开始竞争同一把锁

就像有三个好哥们都在追校花在这里插入图片描述

  1. 其中线程1 率先获取到锁, 此时线程1 通过里层的 if (instance == null) 进一步确认实例是否已经创建. 如果没创建, 就把这个实例创建出来.

其中有个哥们就对校花说:你有男朋友吗?如果没有我做你男朋友吧,这时候其他哥们是在门口等待里面情况的
在这里插入图片描述

  1. 当线程1 释放锁之后, 线程2 和 线程3 也拿到锁, 也通过里层的 if (instance == null) 来确认实例是否已经创建, 发现实例已经创建出来了, 就不再创建了

校花答应了第一个哥们后,那哥们就走了
这时候另外一个哥们又来问:能做你男票不?
校花此时就说:我已经有男朋友了
此时这个哥们就不能成为校花的男朋友了
在这里插入图片描述

  1. 后续的线程, 不必加锁, 直接就通过外层 if (instance == null) 就知道实例已经创建了, 从而不再尝试获取锁了. 降低了开销

那哥们出去后就和追求校花的人说,校花已经有男盆友了
这时候等待的人听见这个消息就不会再进去询问了
在这里插入图片描述

⭕总结

关于《【JavaEE】 饿汉模式与懒汉模式详解与实现》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1092586.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

五子棋(C语言实现)

目录 构思 1、主程序 2、初始化 3、游戏菜单 4、打印棋盘 6、玩家下棋 7、判断输赢 8、功能整合 人机下棋 完整版: game.h game.c text.c 测试功能代码 构思 五子棋不必多介绍了,大家小时候都玩过哈。 我们要通过程序实现这个小游戏&…

AI对网络安全的影响与挑战

近年来,随着人工智能(AI)技术的快速发展,网络安全领域也开始逐渐引入生成式AI应用。根据最新的数据研究,生成式AI对网络安全和合规的影响最大,同时也包括了IT和云的运维、硬件和软件支持领域。通过AI和自动…

Typora--博客必备神器

文章目录 一、下载typora二、创建文件前的操作1.显示扩展名2.typora设置和markdown语法2.1markdown语法2.2图片的管理2.3**高亮**2.4设置自动保存和调试 一、下载typora 进入typota.io网站,点击下载即可。 二、创建文件前的操作 1.显示扩展名 设置显示文件扩展名…

python+django学生选课管理系统_wxjjv

1)前台:首页、课程信息、校园论坛、校园公告、个人中心、后台管理。 (2)管理员:首页、个人中心、学生管理、教师管理课、程信息管理、课程分类管理、选课信息管理、作业信息管理、提交作业管理、学生成绩管理、校园论…

android studio 移植工程

第一步: 第二步:创建 第三步: 第四步:复制文件至替代新工程中的文件 第五步:修改 第六步:编译OK

第 125 场周赛 三元组

可恶,被longlong的长度卡住了,要是longlong再大一点就好了(bushi),其实是算法有问题,里面涉及1e9*1e9*1e9,longlong肯定存不下,一会儿去改改,先记录一下。 题目:竞赛 - …

树莓派玩转openwrt软路由:12.OpenWrt安装MySQL

1、安装MySQL #下载MySQL镜像 docker pull arm64v8/mysql:latest#运行MySQL镜像 docker run --name mysql --restartalways --privilegedtrue \ -v /usr/local/mysql/data:/var/lib/mysql \ -v /usr/local/mysql/conf.d:/etc/mysql/conf.d \ -e MYSQL_ROOT_PASSWORD123456 -p …

C++——C++入门

C 前言一、认识C二、C入门C关键字(C98)命名空间命名空间定义命名空间使用 C输入&输出缺省参数缺省参数概念缺省参数分类 函数重载函数重载概念C支持函数重载的原理--名字修饰(name Mangling) 总结 前言 C的学习开始啦! 来吧~让我们拥抱更广阔的知识海洋&#x…

msvcp140.dll文件下载方法,找不到msvcp140.dll丢失的解决方法

在我日常的计算机维护和故障排除工作中,我遇到了许多由于丢失或损坏MSVCP140.dll文件而导致的程序无法正常运行的问题。这个DLL文件是Microsoft Visual C 2010 Redistributable Package的一部分,它是许多Windows应用程序(尤其是使用C编写的程…

C++11中类与对象推出的新功能 [补充讲解final/override关键字]

文章目录 1.移动构造2.移动赋值对于移动构造/移动赋值的想法 3.类成员定义时初始化4.强制生成默认函数的关键字default5.禁止生成默认函数的关键字delete5.1介绍5.2应用场景1.法一:析构函数私有化2.法二: delete关键字思考 6.final关键字7.override关键字 1.移动构造 编译器自动…

Lambda表达式(JAVA)

注:如果没有学过匿名内部类和接口不推荐往下看。 Lambda表达式的语法: (parameters) -> expression 或 (parameters) ->{ statements; } parameters:表示参数列表;->:可理解为“被用于”的意思&#xff1b…

苹果修复了旧款iPhone上的iOS内核零日漏洞

导语 近日,苹果发布了针对旧款iPhone和iPad的安全更新,回溯了一周前发布的补丁,解决了两个被攻击利用的零日漏洞。这些漏洞可能导致攻击者在受影响的设备上提升权限或执行任意代码。本文将介绍这些漏洞的修复情况以及苹果在修复漏洞方面的持续…

自定义类型:结构体,枚举,联合 (2)

2. 位段 位段的出现就是为了节省空间。 2.1 什么是位段 位段的声明和结构是类似的,有两个不同: 1.位段的成员必须是 int、unsigned int 或signed int 。 2.位段的成员名后边有一个冒号和一个数字。 比如: struct A {int _a:2;int _b:5;int…

ArcGIS笔记5_生成栅格文件时保存报错怎么办

本文目录 前言Step 1 直接保存到指定文件夹会报错Step 2 先保存到默认位置再数据导出到指定文件夹 前言 有时生成栅格文件时,保存在自定义指定的文件夹内会提示出错,而保存到默认位置则没有问题。因此可以通过先保存到默认位置,再数据导出到…

Vite与Webpack谁更胜一筹,谁将引领下一代前端工具的发展

你知道Vite和Webpack吗?也许有不少“程序猿”对它们十分熟悉。 Webpack Webpack是一个JavaScript应用程序的静态模块打包工具,它会对整个应用程序进行依赖关系图构建。而这也会导致一个不可避免的情况,使用Webpack启动应用程序的服务器&…

GO-SLAM——论文简析

GO-SLAM 位姿估计效果很好,有高效的回环检测和 full BA(每个关键帧),适用于单目、双目和 RGB-D。 一、简介 消费级深度传感器容易产生噪声,这就导致 RGB-D SLAM 会丢失一些几何细节,导致过度平滑。使用轻…

ASP.NET framework升级core .NET 6.0

C# ASP.NET framework 升级core .NET 6.0 .NET 7.0 .NET 8.0 或者以上 using System.Web.Http; using HttpPostAttribute Microsoft.AspNetCore.Mvc.HttpPostAttribute; using RouteAttribute Microsoft.AspNetCore.Mvc.RouteAttribute; using HttpGetAttribute Mic…

c++初阶--内存管理

目录 c/c 内存分布c内存管理方式new/delete操作内置类型new和delete操作自定义类型 operator new与operator delete函数new和delete的实现原理内置类型自定义类型 malloc/free和new/delete的区别内存泄露什么是内存泄漏,内存泄露的危害如何避免内存泄漏 在c语言中我…

数据结构 - 4(栈和队列6000字详解)

一:栈 1.1 栈的概念 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原…

Go语言入门心法(三): 接口

Go语言入门心法(一) Go语言入门心法(二): 结构体 一:go语言接口认知 Go语言中接口认知升维:解决人生问题的自我引导法则: 复盘思维|结构化思维|金字塔思维|体系化思维|系统化思维 面向对象编程(oop)三大特性: 封装,继承,多态 Go语言中,可以认为接口是一种自定义的抽…