Java Web 实战 07 - 多线程基础之单例模式
大家好 , 这篇文章给大家带来的是单例模式 , 单例模式中分为懒汉模式和饿汉模式 , 懒汉模式是需要用的到的时候才去创建实例 , 而饿汉模式是程序一启动就立刻创建实例 , 在这其中还有很多其他问题需要我们去研究
推荐大家跳转到这里 , 观看效果更加
上一篇文章的链接我也贴在这里了
单例模式
- 单例模式
- 1. 饿汉模式 : 程序启动立即创建实例
- 2. 懒汉模式 : 程序启动先不创建实例 , 等到真正有用的时候再创建实例
- 3. 正题 : 上述的两个单例模式的代码在多线程环境下调用 getInstance() 是线程安全的吗?
单例模式
单例模式是一种非常常见的设计模式
设计模式就相当于棋谱 , 前人总结出的一些固定套路 , 如果我们按照这些固定套路 , 代码就不会写的太离谱
设计模式虽然很有用 , 但是需要大家有一定的编程经验才好理解一点
未来肯定会经常和设计模式打交道 , 但是现在我们还是学习一下面试中比较常见的设计模式
校招中 , 关于设计模式 , 主要考察两个 : 单例模式、工厂模式
单例模式的目的就是 : 有些对象 , 在一个程序中应该只有一个实例 , 就可以使用单例模式
在单例模式下 , 对象的实例化就被限制了 , 只能创建一个 , 多的就不能再创建了
单例模式就是借助相关语法强制限制咱们不能创建多个实例
开车为啥要系安全带 ?
大家都不想出现事故 , 但是通过司机并不能完全保证安全
所以就出现了安全带 , 一定程度上保证了安全
在 Java 中 , 单例模式有很多种实现方式
我们主要介绍两个 : 饿汉模式 和 懒汉模式
他们俩描述的是 创建实例的时机
饿汉模式 : 程序启动立即创建实例
懒汉模式 : 程序启动先不创建实例 , 等到真正有用的时候再创建实例
大家不要把懒理解成贬义词 , 在计算机中 , 懒这个字往往意味着性能比较高
许多科技的发明都是因为人类懒 , 想要方便生活
计算机中 , 这种思想很常见
硬盘里面有许多大文件
使用文件资源管理器打开它
- 饿汉模式 : 把这些大文件通通读到内存中 , 读取完毕之后再让用户进行查看和修改
- 懒汉模式 : 只读取出当前页面能显示出的范围的文件数目 , 随着用户翻页 , 继续再读后面内容 , 用户还没翻到 , 就不着急度
或者刷抖音 , 不是一下子加载出所有的视频 , 而是一个一个加载 , 刷完了再加载下一条
或者微信朋友圈 , 只有当你点进去的时候 , 才会加载原图 , 否则就不加载
1. 饿汉模式 : 程序启动立即创建实例
// 单例模式的实现
// 饿汉模式
class Singleton {// 创建实例,把创建的实例赋值给一个静态变量// 静态变量又叫做类变量,类对象在整个程序中只有唯一一个实例->保证了单例private static Singleton instance = new Singleton();// 通过这个静态方法返回独苗实例// 后续如果需要使用这个实例,统一基于getInstance方法来获取咱们的独苗实例,就不需要去new了public static Singleton getInstance() {return instance;}// 构造方法设为私有:其他的类想来new就不行了// 从根本上禁止了其他代码来new Singleton 实例了private Singleton() {}
}public class Demo19 {public static void main(String[] args) {Singleton instance1 = Singleton.getInstance();Singleton instance2 = Singleton.getInstance();// 创建的是同一个实例System.out.println(instance1 == instance2);// true}
}
如果我们再手动创建一个实例 , 就会报错

单例模式的关键是 : 使用静态成员表示实例 (唯一性) + 让构造方法为私有 (堵住了 new 创建新实例的方式)
那么这段代码为什么说是饿汉模式呢 ?

2. 懒汉模式 : 程序启动先不创建实例 , 等到真正有用的时候再创建实例
// 单例模式的实现
// 懒汉模式
class SingletonLazy {// 先不着急实例化private static SingletonLazy instance = null;public static SingletonLazy getInstance() {// 进行判断// 为空->创建实例if(instance == null) {instance = new SingletonLazy();}return instance;}// 构造方法同样也是 private 的private SingletonLazy() {}
}public class Demo19 {public static void main(String[] args) {Singleton instance1 = Singleton.getInstance();Singleton instance2 = Singleton.getInstance();}
}

3. 正题 : 上述的两个单例模式的代码在多线程环境下调用 getInstance() 是线程安全的吗?
饿汉模式是天然线程安全的

懒汉模式是线程不安全的

懒汉模式一旦实例创建好之后 , 后续 if 条件就进不去了 , 此时也就全是读操作了 , 也就线程安全了
所以我们要是想让懒汉模式变成线程安全的 , 就一定要解决第一次创建实例的过程
那如何解决懒汉模式的线程不安全问题呢 ?
解决办法就是加锁
// 单例模式的实现
// 懒汉模式
class SingletonLazy {// 先不着急实例化private static SingletonLazy instance = null;public static SingletonLazy getInstance() {// 进行判断// 为空->创建实例synchronized (SingletonLazy.class) {if(instance == null) {instance = new SingletonLazy();}}return instance;}// 构造方法同样也是 private 的private SingletonLazy() {}
}public class Demo19 {public static void main(String[] args) {Singleton instance1 = Singleton.getInstance();Singleton instance2 = Singleton.getInstance();}
}

但是目前还是有问题
懒汉模式只是初始情况下 , 才会有线程不安全问题
一旦实例创建好了之后 , 此时就安全了
既然如此 , 后续再调用 getInstance 的时候就不应该再去加锁了
当线程已经安全了 , 我们再尝试进行加锁 , 其实就非常影响效率了
所以我们需要做到 : 该加锁的地方需要加锁 , 不该加锁的时候别乱加锁
我们只需要再加上一层 if 判定就 ok 了
// 单例模式的实现// 懒汉模式
class SingletonLazy {// 先不着急实例化private static SingletonLazy instance = null;public static SingletonLazy getInstance() {// 进行判断// 为空->创建实例if(instance == null) {synchronized (SingletonLazy.class) {if(instance == null) {instance = new SingletonLazy();}}}return instance;}// 构造方法同样也是 private 的private SingletonLazy() {}
}public class Demo19 {public static void main(String[] args) {Singleton instance1 = Singleton.getInstance();Singleton instance2 = Singleton.getInstance();}
}


外层 if 为了降低加锁的频率 , 降低锁冲突的概率 . 里层 if 才是真正判定是否要实例化
我们再举个栗子帮大家理清楚为什么要用双层判断

其中 , 一个线程读 , 一个线程写还存在内存可见性问题

// 单例模式的实现
// 懒汉模式
class SingletonLazy {// 先不着急实例化volatile private static SingletonLazy instance = null;public static SingletonLazy getInstance() {// 进行判断// 为空->创建实例if(instance == null) {synchronized (SingletonLazy.class) {if(instance == null) {instance = new SingletonLazy();}}}return instance;}// 构造方法同样也是 private 的private SingletonLazy() {}
}public class Demo19 {public static void main(String[] args) {Singleton instance1 = Singleton.getInstance();Singleton instance2 = Singleton.getInstance();}
}

对于 volatile , 有一种大家认同比较多的说法是这样
volatile 在此处的用途 , 是禁止指令重排序

关于这里 , 还有可能问到对象存储在哪里的问题 , 我们也需要格外关注一下

单例模式的分析就到这里了 , 觉得有收获的请一键三连嗷~
相关文章:
Java Web 实战 07 - 多线程基础之单例模式
大家好 , 这篇文章给大家带来的是单例模式 , 单例模式中分为懒汉模式和饿汉模式 , 懒汉模式是需要用的到的时候才去创建实例 , 而饿汉模式是程序一启动就立刻创建实例 , 在这其中还有很多其他问题需要我们去研究 推荐大家跳转到这里 , 观看效果更加 上一篇文章的链接我也贴在这…...
uniapp上实现左右关联滚动
先看效果: 代码: <template><view class"container"><!-- 左侧fixed导航区域 --><view class"left"><viewv-for"item in leftList":key"item.id"class"left_item":class…...
Docker Remote API未授权访问
目录Docker简述Docker 2375端口安全风险Docker命令连接利用声明:本文仅供学习参考,其中涉及的一切资源均来源于网络,请勿用于任何非法行为,否则您将自行承担相应后果,本人不承担任何法律及连带责任。Docker简述 Docke…...
【蓝桥杯】第十四届蓝桥杯模拟赛(第三期)C++ (弱go的记录,有问题的话求指点)
博主是菜鸡啦,代码仅供参考,只确定能过样例,嘻嘻~第一题,填空题问题描述请找到一个大于 2022 的最小数,这个数转换成十六进制之后,所有的数位(不含前导 0)都为字母(A 到 …...
算法24:LeetCode_并查集相关算法
目录 题目一:力扣547题,求省份数量 题目二:岛屿数量 题目三:岛屿数量拓展 什么是并查集,举个简单的例子。学生考试通常会以60分为及格分数,我们将60分及以上的人归类为及格学生,而60分以下归…...
TypeScript核心知识点
TypeScript 核心 类型注解 知道:TypeScript 类型注解 示例代码: // 约定变量 age 的类型为 number 类型 let age: number 18 age 19: number 就是类型注解,它为变量提供类型约束。约定了什么类型,就只能给该变量赋值什么类型的…...
基于“遥感+”融合技术在碳储量、碳收支、碳循环等多领域监测与模拟实践
以全球变暖为主要特征的气候变化已成为全球性环境问题,对全球可持续发展带来严峻挑战。2015年多国在《巴黎协定》上明确提出缔约方应尽快实现碳达峰和碳中和目标。2019年第49届 IPCC全会明确增加了基于卫星遥感的排放清单校验方法。随着碳中和目标以及全球碳盘点的现…...
外卖点餐系统小程序 PHP+UniAPP
一、介绍 本项目是给某大学餐厅开发的外面点餐系统,该项目针对校内的学生,配送由学校的学生负责配送。因此,该项目不同于互联网的外卖点餐系统。 该系统支持属于 Saas 系统,由平台端、商家端、用户端、以及配送端组成。 其中&a…...
vuex3的介绍与state、actions和mutations的使用
一、定义官网:Vuex 是什么? | Vuex (vuejs.org)Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。二、安装cdn<script src"/path/…...
windows 自带端口转发
使用Portproxy模式下的Netsh命令即能实现Windows系统中的端口转发,转发命令如下: netsh interface portproxy add v4tov4 listenaddress[localaddress] listenport[localport] connectaddress[destaddress]listenaddress – 等待连接的本地ip地址 listenport – 本…...
【算法】算法基础入门详解:轻松理解和运用基础算法
😀大家好,我是白晨,一个不是很能熬夜😫,但是也想日更的人✈。如果喜欢这篇文章,点个赞👍,关注一下👀白晨吧!你的支持就是我最大的动力!Ǵ…...
2.9.1 Packet Tracer - Basic Switch and End Device Configuration(作业)
Packet Tracer - 交换机和终端设备的基本 配置地址分配表目标使用命令行界面 (CLI),在两台思科互联网络 操作系统 (IOS) 交换机上配置主机名和 IP 地址。使用思科 IOS 命令指定或限制对设备 配置的访问。使用 IOS 命令来保存当前的运行配置。配置两台主机设备的 IP …...
AtCoder Beginner Contest 216(F)
F - Max Sum Counting 链接: F - Max Sum Counting 题意 两个 大小为 nnn 的序列 aiaiai 和 bibibi,任意选取一些下标 iii,求 max(ai)>∑bi\max(ai) > \sum{bi}max(ai)>∑bi的方案数。 解析 首先考虑状态 一是和,…...
每天学一点之Stream流相关操作
StreamAPI 一、Stream特点 Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,负责存储数据,Stream流讲的是计算,负责处理数据!” 注意: ①Str…...
MatCap模拟光照效果实现
大家好,我是阿赵 之前介绍过各种光照模型的实现方法。那些光照模型的实现虽然有算法上的不同,但基本上都是灯光方向和法线方向的计算得出的明暗结果。 下面介绍一种叫做MatCap的模拟光照效果,这种方式计算非常简单,脱离灯光的计算…...
二十一、PG管理
一、 PG异常状态说明 1、 PG状态介绍 可以通过ceph pg stat命令查看PG当前状态,健康状态为“active clean” [rootrbd01 ~]# ceph pg stat 192 pgs: 192 activeclean; 1.3 KiB data, 64 MiB used, 114 GiB / 120 GiB avail; 85 B/s rd, 0 op/s2、pg常见状态 状…...
SAPUI5开发01_01-Installing Eclipse
1.0 简要要求概述: 本节您将安装SAPUI 5,以及如何在Eclipse Juno中集成SAPUI 5工具。 1.1 安装JDK JDK 是一种用于构建在 Java 平台上发布的应用程序、Applet 和组件的开发环境,即编写 Java 程序必须使用 JDK,它提供了编译和运行 Java 程序的环境。 在安装 JDK 之前,首…...
Qt之高仿QQ系统设置界面
QQ或360安全卫士的设置界面都是非常有特点的,所有的配置项都在一个垂直的ScrollArea中,但是又能通过左侧的导航栏点击定位。这样做的好处是既方便查看指定配置项,又方便查看所有配置项。 一.效果 下面左边是当前最新版QQ的系统设置界面,右边是我的高仿版本,几乎一毛一样…...
JVM概览:内存空间与数据存储
核心的五个部分虚拟机栈:局部变量中基础类型数据、对象的引用存储的位置,线程独立的。堆:大量运行时对象都在这个区域存储,线程共享的。方法区:存储运行时代码、类变量、常量池、构造器等信息,线程共享。程…...
固态存储设备固件升级方案
1. 前言 随着数字化时代的发展,数字数据的量越来越大,相应的数据存储的需求也越来越大,存储设备产业也是蓬勃发展。存储设备产业中,发展最为迅猛的则是固态存储(Solid State Storage,SSS)。数字化时代,海量…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...

