浅浅了解下单例模式中的懒汉模式饿汉模式

news2024/12/26 22:55:48

单例模式

    • 1.什么是设计模式
    • 2.什么是单例模式
    • 3.常见实现单例模式的两种方式
      • 1.饿汉模式
        • (1)特点
        • (2)代码实现
        • (3)线程是否安全
      • 2.懒汉模式
        • (1)特点
        • (2)代码实现
        • (3)线程是否安全
        • (4)如何保证线程安全
          • 解决方案:
          • 进阶方案
      • 3.对比懒汉模式和饿汉模式
        • 1、线程安全
        • 2、是否延迟加载
        • 3、系统开销
        • 4、不同类加载器加载的问题

1.什么是设计模式

想必大家都听说过孙子兵法吧,尤其是电视剧狂飙又带火了这本书,我们就可以将孙子兵法理解为"设计"模式,因为它里面的计谋是反复被世人使用,多数人知晓,带兵打仗的总结.

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。

简单说:

模式: 在某些场景下针对某类问题的某种通用的解决方案.

  • 场景:项目所在的环境
  • 问题:约束条件,项目目标等
  • 解决方案:通用,可复用的设计,解决约束达到目标.

—上述内容采纳于 点击此处

2.什么是单例模式

保证某个类在程序中至多存在一个实例.

在单例模式中一般只提供一个getInstance()方法来获取类的实例对象,不允许通过其他方法创建或者获取该实例对象.

其中单例模式中常见的两种设计模式是懒汉模式饿汉模式.

3.常见实现单例模式的两种方式

1.饿汉模式

(1)特点

先通过生活中的常见举例:

这个饿汉模式的 “饿” 在这里表示急迫的意思,大家都知道,如果自己饿了之后见到食物就会狼吞虎咽,那狼吞虎咽这个过程是不是就表示很急迫.

就像咱们平时放暑假的暑假作业一样,如果是饿汉模式的话,就会迫不及待的把作业全部写完再干别的事情.

(2)代码实现

//单例模式
class Singleton{
    //唯一实例的本体
    private static Singleton instance = new Singleton();
    public static Singleton getSingleton(){
        return instance;
    }
    private Singleton(){};//构造方法设置为私有,这样的话就不可以从外部去new

}
  • 通过private修饰Singleton属性,只可以通过getSingleton()方法来获取该实例.
  • 由于构造方法被private修饰,所以这个类是不可以被new的.
  • 由于Singleton属性是被static修饰的,所以是在类加载时创建的
  • getSingleton()方法也是static修饰的,属于静态方法可以通过类名.getSingleton()调用
  • static修饰表示该方法/属性是通过类名调用的,与实例无关,即类加载好后就在内存中存在的,有且只有一份.

(3)线程是否安全

在类加载的时候就已经实例化了,所以该实例化没有涉及到实例化的修改操作,只是进行读取操作即多个线程读取同一个变量。在多线程情况下是线程安全的。

2.懒汉模式

(1)特点

咱们还是通过上述的暑假作业来讲解,懒汉模式,"懒"大家就可以联想到,一个懒人,非必要不写作业,说的也就是我,平时都是在暑假最后一天熬油点灯的.

那么在程序中这个 “懒” 是什么意思呢?

大家都看过小说吧,一本小说好几十万字,如果大家在看小说时,只是想看一部分,但是却把整本小说的内容都读取到了内存中,那是不是很浪费资源,并且效率很慢…所以呢,就有大佬想到了一个方法,咱们小说不是一页一页翻的嘛,他就只是提前加载出后两页的内容,当你前面的看完之后再去加载后面的内容,这样就很大程度的减少了资源的浪费,并且提高了效率.

总结:什么时候用,什么时候创建!

(2)代码实现

class SingletonLazy{
  private static SingletonLazy instance;
  
    public static SingletonLazy getInstance(){
            if(instance == null){
                instance = new SingletonLazy();
         	 }
            return instance;
    }
    //构造方法设置为私有,这样的话就不可以从外部去new
    private SingletonLazy(){
	
	};

}

(3)线程是否安全

上述线程是不安全的,前面的博客写到过,多个线程修改同一个变量是会产生线程安全问题的.

(4)如何保证线程安全

我们先通过图片了解它为什么是线程不安全的
在这里插入图片描述
可以发现,我明明只想创建一个实例,但是却创建了两个,如果线程更多的话可能会创建更多的实例,这就涉及到了线程的安全问题,假设一个实例对象就很大,那多创建的一个甚至是多个会浪费多少资源都不得而知…

解决方案:

我们可以通过加锁的方式来保证代码的原子性,此时即使是多线程同时调用getSingleton()也不会出错~

    public static SingletonLazy getInstance(){
    	synchronized(SingletonLazy.class)
            if(instance == null){
                instance = new SingletonLazy();
         	 }
         }
            return instance;
    }

在这里插入图片描述

  • 优点:保证了线程安全
  • 缺点:由于只需要实例一次对象,在此之后调用该方法都不会new,此时再去加锁就会浪费资源,降低效率
进阶方案

由于我们只有在第一次new的时候需要保证原子性,那么我们可以通过双重if方法来保证效率,代码如下:

  public static SingletonLazy getInstance(){
        if(instance == null){
            synchronized (SingletonLazy.class){
                if(instance == null){
                    instance = new SingletonLazy();
                }
            }
        }
        //除了第一次new的时候,其余都是只进行一次if就好
        return instance;
    }

虽然上面的if出现了两次,且内容一样,但是他们想要达到的目的是不同的.因为是在不同的时间段.

举例:我们去找实习是不是需要面试,可能此次的面试失败了,但是在后续的面试中,由于不在同一时间段,我的知识储备量大大增加,所以就有可能会面试成功

在这里插入图片描述

  • 第一个if判断是否为空,如果不为空的话直接返回,就不再进行加锁的操作(省去了多余的加锁操作,提高了效率)
  • 如果进入了第二个if的话,此时线程1创建实例是原子操作,当线程1释放锁后,线程2的instance已经不是null了,直接返回创建好的对象.

还有一点点的问题就是指令重排序,虽然该方法在编译器中是为了提高效率,但是也会引带出一些问题.

我们将new操作分为三个部分:

1.分配内存空间
2.实例化对象
3.给变量赋值

正常情况下如下图:
在这里插入图片描述

异常情况:

在这里插入图片描述
解决方法:

//通过volatile修饰该属性
  volatile private static SingletonLazy instance;

3.对比懒汉模式和饿汉模式

1、线程安全

懒汉式需要使用同步锁来保证线程安全,而饿汉式本身就是线程安全的。

2、是否延迟加载

懒汉式是延迟加载的,而饿汉式是立即加载的。

3、系统开销

懒汉式可以避免在程序启动时就创建单例对象,从而降低系统的开销。而饿汉式会在程序启动时就创建单例对象,如果单例对象很大或需要一定时间才能创建,那么会造成一定的系统开销。

4、不同类加载器加载的问题

如果有多个类加载器,那么懒汉式会存在不同类加载器加载了不同的单例对象的情况。而饿汉式不存在这个问题。

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

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

相关文章

QMainWindow

文章目录 QMainWindow基本元素QMainWindow函数介绍简单的示例效果图 QMainWindow QMainWindow是一个为用户提供主窗口程序 的类,包含一个菜单栏(menu bar)、多个工具栏 (tool bars)、多个锚接部件(dock widgets)、―个 状态栏(status bar )及一个中心部件(central …

【算法提高:动态规划】1.2 最长上升子序列模型(TODO最长公共上升子序列)

文章目录 题目列表1017. 怪盗基德的滑翔翼1014. 登山482. 合唱队形1012. 友好城市(⭐排序后 最长上升子序列模型)1016. 最大上升子序列和1010. 拦截导弹解法1——最长递减子序列 贪心解法2——最长递减子序列 最长递增子序列(⭐贪心结论&am…

K8s集群部署-详细步骤

不够详细,后面有时间再编辑 安装 关闭防火墙 systemctl stop firewalld systemctl disable firewalld 关闭swap, selinux swapoff -a && sed -i / swap / s/^\(.*\)$/#\1/g /etc/fstab setenforce 0 && sed -i s/^SELINUX.*/SELINUXdisabled/ /…

Safari 查看 http 请求

文章目录 1、开启 Safari 开发菜单2、显示 JavaScript 控制台 1、开启 Safari 开发菜单 Safari 设置中,打开开发菜单选项 *** 选择完成后,Safari 的目录栏就会出现一个 开发 功能。 2、显示 JavaScript 控制台 开启页面后,在开发中选中 显…

Android 之 动画合集之补间动画

本节引言: 本节带来的是Android三种动画中的第二种——补间动画(Tween),和前面学的帧动画不同,帧动画 是通过连续播放图片来模拟动画效果,而补间动画开发者只需指定动画开始,以及动画结束"关键帧"&#xff0…

提示计算机丢失MSVCP140.dll怎么办?这三个修复方法可解决

最近在使用电脑的过程中,遇到了一个问题,即缺少了MSVCP140.dll文件。这个文件是一个动态链接库文件,常用于Windows操作系统中的应用程序中。由于缺少这个文件,会导致计算机系统无法运行某些软件或游戏。丢失MSVCP140.dll可能是由于…

【技术分享】oracle数据库相关操作

-- 截断表 TRUNCATE TABLE TABLE_NAME;-- 删除表 DROP TABLE TABLE_NAME;-- 查询表 SELECT * FROM TABLE_NAME;-- 添加一条记录 INSERT INTO TABLE_NAME(COLUMN) VALUES(VALUE);-- 删除记录 DELETE FROM TABLE_NAME WHERE COLUMNVALUE;-- 修改记录 UPDATE TABLE_NAME SET…

Android性能优化之Thread native层源码分析(InternalError/Out of memory)

近期处理Bugly上OOM问题,很多发生在Thread创建启动过程,虽然最后分析出是32位4G虚拟内存不足导致,但还是分析下Java层Thread 源码过程,可能会抛出的异常InternalError/Out of memory。 Thread报错堆栈: Java线程创建…

数据库|手把手教你成为 TiDB 的 Contributor

一、背景 最近笔者在 AskTUG 回答问题的时候发现,在 6.5.0 版本出现了几个显示未启动必要组件 NgMonitoring 的问题贴。经过排查发现,是 ngmonitoring.toml 中的配置文件出现了问题。文件中的 endpoints 应该是以逗号分隔的,但是却写成了以空…

JavaWeb 项目实现(二) 注销功能

3.注销功能 接前篇,实现了登录功能之后,现在实现注销功能。 因为我们实现登录就是在Session中记录了用户信息。 所以注销功能,就是在Session中移除用户信息。 代码:删除Session中的用户信息,跳转登录页面 package…

【安全渗透】第一次作业(编码知识总结)

目录 1. ASCII编码 2、Unicode 3、UTF-8 1. ASCII编码 ASCII 是“American Standard Code for Information Interchange”的缩写,翻译过来是“美国信息交换标准代码”。ASCII 的标准版本于 1967 年第一次发布,最后一次更新则是在 1986 年&#xff0c…

QEMU源码全解析13 —— QOM介绍(2)

接前一篇文章:QEMU源码全解析12 —— QOM介绍(1) 本文内容参考: 《趣谈Linux操作系统》 —— 刘超,极客时间 《QEMU/KVM》源码解析与应用 —— 李强,机械工业出版社 特此致谢! 本回开始对QOM…

django学习笔记(1)

django创建项目 先创建一个文件夹用来放django的项目,我这里是My_Django_it 之后打开到该文件下,并用下面的指令来创建myDjango1项目 D:\>cd My_Django_itD:\My_Django_it>"D:\zzu_it\Django_learn\Scripts\django-admin.exe" startpr…

记录每日LeetCode 2500.删除每行中的最大值 Java实现

题目描述: 给你一个 m x n 大小的矩阵 grid ,由若干正整数组成。 执行下述操作,直到 grid 变为空矩阵: 从每一行删除值最大的元素。如果存在多个这样的值,删除其中任何一个。 将删除元素中的最大值与答案相加。 …

Reinforcement Learning with Code 【Chapter 7. Temporal-Difference Learning】

Reinforcement Learning with Code This note records how the author begin to learn RL. Both theoretical understanding and code practice are presented. Many material are referenced such as ZhaoShiyu’s Mathematical Foundation of Reinforcement Learning, . 文章…

Linux内核与内核空间是什么关系呢?

对内核空间的认识清晰了许多。要理解用户空间与内核空间需要有如下的几个认识: 内核的认识:从2个不同的角度来理解,一个是静态的角度,如“芦中人”所比喻,内核可以看做是一个lib库,内核对外提供的API打包…

快速远程桌面控制公司电脑远程办公

文章目录 快速远程桌面控制公司电脑远程办公**第一步****第二步****第三步** 快速远程桌面控制公司电脑远程办公 远程办公的概念很早就被提出来,但似乎并没有多少项目普及落实到实际应用层面,至少在前几年,远程办公距离我们仍然很遥远。但20…

1分钟上手Apifox

1、客户端右上角账号设置-生成令牌 2、IDEA下载插件 Apifox Helper 3、 配置ApiFoxHelper 令牌 4、在controller类界面右键 5、输入项目id 6、项目ID从客户端 项目设置-项目ID获取 7、导入成功 8、右键刷新查看导入的接口 9、自动生成数据(某postman还要自己手输&a…

说一说java中的自定义注解之设计及实现

一、需求背景 比如我们需要对系统的部分接口进行token验证,防止对外的接口裸奔。所以,在调用这类接口前,先校验token的合法性,进而得到登录用户的userId/role/authority/tenantId等信息;再进一步对比当前用户是否有权…

MyBatis 快速入门【中】

😀前言 本篇博文是MyBatis(简化数据库操作的持久层框架)–快速入门[上]的核心部分,分享了MyBatis实现sql的xml配置和一些关联配置、异常分析 🏠个人主页:晨犀主页 🧑个人简介:大家好,我是晨犀&a…