多线程应用——单例模式
单例模式
文章目录
- 单例模式
- 一.什么是单例模式
- 二.如何实现
- 1.口头实现
- 2.利用语法特性
- 三.实现方式(饿汉式+懒汉式)
- 1.饿汉式
- 2.懒汉式
- 3.线程安全的单例模式
- 4.双重检查锁
- 5.禁止指令重排序
一.什么是单例模式
单例模式(Singleton Pattern)顾名思义,在程序中一个类只有一个对象实例。例如我们在JDBC编程中,我们创建了一个简单类DataSource,只要从DataSource中获取数据库连接即可,不用创建多个DataSource对象。
单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供了一个全局访问点来访问该实例。
二.如何实现
1.口头实现
2.利用语法特性
- 本质上就是利用编程语言自身的特性,强行限制某个类不能创建多个实例
- static修饰一个变量后,这个变量就从一个普通的成员变量属性变成了类对象的成员变量
- 在JVM中一个类只要一个类对象,从而保证了static变量的唯一性
三.实现方式(饿汉式+懒汉式)
1.饿汉式
public class SingletonHungry{//类的成员变量private static Singleton instance=new Singleton();//私有化构造方法private SingletonHungry(){ }/*** 对外获取类成员方法* @return*/public static SingletonHungry getInstance(){return instance;}
}
饿汉式:需要急迫的创建这个实例,类在加载的过程中就创建出来了
描述:这种方式比较常见,但容易产生垃圾对象
- 优点:没有加锁,执行效率高
- 缺点:类加载时就初始化,浪费内存
2.懒汉式
public class SingletonLazy{//类的成员变量private static Singleton instance=null;//私有化构造方法private Singleton(){ }/*** 对外获取类成员方法* @return*/public static Singleton getInstance(){//判断一个需要返回的对象是否为空if (instance==null){//创建对象instance=new SingletonLazy();}//返回单例对象return instance;}
}
懒汉式:什么时候用什么时候才去创建,不要程序启动的时候创建,从而节省了程序启动时的开销
3.线程安全的单例模式
在多线程中,饿汉式只是获取变量而不是修改变量;而懒汉式是修改共享变量,因此存在线程安全问题。
我们用上面的代码做一测试
public class Demo_SingletonLazy {public static void main(String[] args) {//多个线程获取单例对象for (int i = 0; i < 10; i++) {Thread thread = new Thread(() -> {SingletonLazy instance = SingletonLazy.getInstance();System.out.println(instance);});thread.start();}}
}

我们知道造成线程安全问题的原因有 原子性、内存可见性、有序性

通过上图分析得出问题:不满足原子性,那该如何解决呢,当然是加锁。
public class SingletonLazy{//类的成员变量private static Singleton instance=null;//私有化构造方法private Singleton(){ }/*** 对外获取类成员方法* @return*/public static Singleton getInstance(){synchronized(SingletonLazy.class){//判断一个需要返回的对象是否为空if (instance==null){//创建对象instance=new SingletonLazy();}}//返回单例对象return instance;}
}

加锁之后,我们看到问题也解决了,但此时还有一个非常严重的问题:效率问题
- 当变量没有初始化时,第一次创建可能会出现线程问题,因为多个线程可能创建实例
- 当实例变量被创建后,new操作将永远不会执行了,因为获取到的实例不为null了
- 那么synchronized的锁就没有必要加了,因为实例已经创建好了,之后线程拿到锁之后只是判断一下实例是否为空,不会去new了,如果不为null就什么也不干就把锁释放了,这样一来锁白加了,资源也白白浪费了
synchronizeed看上去是一个关键字,可能会涉及到用户态–>内核态之间的切换,这个成本是比较高的,我们为了保证程序正确执行的基础可以承担这个成本,但是没有必要做无用的消耗
4.双重检查锁
既然在第一次创建完实例后加锁是为了判断实例是否为空,那么不如将判断为空放到加锁之前,避免因为上述原因而造成资源浪费
public class SingletonDCL {//定义一个类的成员变量private static SingletonDCL instance=null;private SingletonDCL(){}public static SingletonDCL getInstance(){//第一层判断是否需要加锁if (instance==null){synchronized (SingletonDCL.class){//第二层加锁判断是否需要创建对象if (instance==null){//创建对象instance=new SingletonDCL();}}}//返回单例对象return instance;}
}
5.禁止指令重排序
上述代码还存在一个严重问题,那就是指令重排序问题
假设一个线程在调用getInstande()方法时,拿到了锁,进入了第二层开始new对象:
new对象本质分为三步:
- 申请内存空间
- 调用构造方法,初始化实例
- 把内存首地址赋给对象的引用
可以看出1和3有逻辑关系,2是在这个内存空间里填充数据
如果这里指令重排序,造成执行顺序为1 3 2 那么这个时候又有一个线程执行到第一层的判断,这里的instance就不为空了,返回一个没有完成初始化的对象。这种情况也是很危险的
为了防止指令重排序,给变量加入关键字volatile
public class SingletonDCL {//定义一个类的成员变量private static volatile SingletonDCL instance=null;//禁止指令重排序,也保证了在对共享变量修改时的内存可见性private SingletonDCL(){}public static SingletonDCL getInstance(){//第一层判断是否需要加锁if (instance==null){synchronized (SingletonDCL.class){//第二层加锁判断是否需要创建对象if (instance==null){//创建对象instance=new SingletonDCL();}}}//返回单例对象return instance;}
}
看完留个三连吧
相关文章:
多线程应用——单例模式
单例模式 文章目录 单例模式一.什么是单例模式二.如何实现1.口头实现2.利用语法特性 三.实现方式(饿汉式懒汉式)1.饿汉式2.懒汉式3.线程安全的单例模式4.双重检查锁5.禁止指令重排序 一.什么是单例模式 单例模式(Singleton Patternÿ…...
几种在JavaScript中创建对象的方式!
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 字面量方式⭐ 构造函数方式⭐ Object.create()方式⭐ 工厂函数方式⭐ ES6类方式⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门…...
java项目mysql转postgresql
特殊函数 : mysql: find_in_set(?, ancestors) postgresql: ? ANY (string_to_array(ancestors,,)) mysql: date_format(t1.oper_time, %Y-%m-%d) postgresql: rksj::date to_char(inDate,YYYY-MM-DD) mysql&am…...
SpringBoot Mybatis 多数据源 MySQL+Oracle
一、背景 在SpringBoot Mybatis 项目中,需要连接 多个数据源,连接多个数据库,需要连接一个MySQL数据库和一个Oracle数据库 二、依赖 pom.xml <dependencies><dependency><groupId>org.springframework.boot</groupId&…...
(笔记五)利用opencv进行图像几何转换
参考网站:https://docs.opencv.org/4.1.1/da/d6e/tutorial_py_geometric_transformations.html (1)读取原始图像和标记图像 import cv2 as cv import numpy as np from matplotlib import pyplot as pltpath r"D:\data\flower.jpg&qu…...
【Flutter】Flutter 使用 fluttertoast 实现显示 Toast 消息
【Flutter】Flutter 使用 fluttertoast 实现显示 Toast 消息 文章目录 一、前言二、安装和基础使用三、不同平台的支持情况四、如何自定义 Toast五、在实际业务中的应用六、完整的业务代码示例(基于 Web 端)七、总结 一、前言 在这篇文章中,…...
nowcoder NC236题 最大差值
目录 题目描述: 示例1 示例2 题干解析: 暴力求解: 代码展示: 优化: 代码展示: 题目跳转https://www.nowcoder.com/practice/a01abbdc52ba4d5f8777fb5dae91b204?tpId128&tqId33768&ru/exa…...
TCP/IP五层模型、封装和分用
1.网络通信基础2.协议分层OSI七层协议模型TCP/IP五层/四层协议模型【重点】 3. 封装&分用 1.网络通信基础 IP地址:表示计算机的位置,分源IP和目标IP;举个例子:买快递,商家从上海发货,上海就是源IP&…...
LeetCode 面试题 01.08. 零矩阵
文章目录 一、题目二、C# 题解 一、题目 编写一种算法,若M N矩阵中某个元素为0,则将其所在的行与列清零。 点击此处跳转题目。 示例 1: 输入: [ [1,1,1], [1,0,1], [1,1,1] ] 输出: [ [1,0,1], [0,0,0], [1,0,1] ] 示…...
Qt应用开发(基础篇)——进度条 QProgressBar
一、前言 QProgressBar类继承于QWidget,是一个提供了横向或者纵向进度条的小部件。 QProgressBar进度条一般用来显示用户某操作的进度,比如烧录、导入、导出、下发、上传、加载等这些需要耗时和分包的概念,让用户知道程序还在正常的执行中。 …...
108页石油石化5G智慧炼化厂整体方案PPT
导读:原文《108页石油石化5G智慧炼化厂整体方案PPT》(获取来源见文尾),本文精选其中精华及架构部分,逻辑清晰、内容完整,为快速形成售前方案提供参考。以下是部分内容,...
Codeforces 1625E2 括号树 + BIT
题意 传送门 Codeforces 1625E2 Cats on the Upgrade (hard version) 题解 首先利用栈将原始字符串转换为合法的 RBS,不能匹配的括号设为 ‘.’。根据匹配的括号序列构造树,具体而言,遇到左括号,则新建节点向下递归,…...
PHP命令行CLI的使用
PHP命令行界面 PHP命令行界面(CLI)是一种使用命令行(终端)来运行PHP脚本的方式,与在Web服务器环境下运行PHP不同。CLI提供了一种与操作系统交互的方式,能够在命令行中直接执行PHP代码。 以下是一些与PHP命…...
近期嵌软线下笔试题记录
1、以下代码的输出结果是? #include <stdio.h> #include <string.h>int main() {int a,b,c,d;a 10;b a; //a先赋值给b,然后自增1c a; //a自增1后赋值给cd 10*a; //先进行运算然后a自增1printf("b,c,d:%d…...
基于MYSQL的主从同步和读写分离
目录 一.完成MySQL主从同步(一主两从) 1.主库配置 2.建立同步账号 3.锁表设置只读 4.备份数据库数据 5.主库备份数据上传到从库 6.从库上还原备份 7.解锁 8.从库上设定主从同步 9.启动从库同步开关 10.检查状态 二.基于MySQL一主两从配置&…...
java八股文面试[多线程]——合适的线程数是多少
知识来源: 【并发与线程】 合适的线程数量是多少?CPU 核心数和线程数的关系?_哔哩哔哩_bilibili 【2023年面试】程序开多少线程合适_哔哩哔哩_bilibili...
Linux系统下vim常用命令
一、基础命令: v:可视模式 i:插入模式 esc:命令模式下 :q :退出 :wq :保存并退出 ZZ:保存并退出 :q! :不保存并强制退出二、在Esc下: dd : 删除当前行 yy:复制当前行 p:复制已粘贴的文本 u:撤销上一步 U:…...
【2023】LeetCode HOT 100——链表
目录 1. 相交链表1.1 C++实现1.2 Python实现1.3 时空分析2. 反转链表2.1 C++实现2.2 Python实现2.3 时空分析3. 回文链表3.1 C++实现3.2 Python实现3.3 时空分析4. 环形链表4.1 C++实现4.2 Python实现4.3 时空分析5. 环形链表 II5.1 C++实现5.2 Python实现...
智能井盖传感器,物联网智能井盖系统
随着城市人口的不断增加和城市化进程的不断推进,城市基础设施的安全和可靠性变得愈发重要,城市窨井盖作为城市基础设施重要组成部分之一,其安全性事关城市安全有序运行和居民生产生活安全保障。 近年来,各地都在加强城市窨井盖治理…...
C语言三子棋解析
目录(标2的是我自己写的一堆问题不知道怎么改) 开始菜单1打印棋盘1玩家下棋1电脑下棋1判断输赢1开始菜单2打印棋盘2选择先后2玩家下棋2电脑下棋2判断输赢2完整代码文件else.h文件else.c文件test.c 开始菜单1 void menu()//打印菜单 {printf("*****…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...
