当前位置: 首页 > news >正文

多线程应用——单例模式

单例模式

文章目录

  • 单例模式
    • 一.什么是单例模式
    • 二.如何实现
      • 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();}}
}

image-20230830183534647

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

image-20230830185305419

通过上图分析得出问题:不满足原子性,那该如何解决呢,当然是加锁。

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;}
}

image-20230830185941493

加锁之后,我们看到问题也解决了,但此时还有一个非常严重的问题:效率问题

  1. 当变量没有初始化时,第一次创建可能会出现线程问题,因为多个线程可能创建实例
  2. 当实例变量被创建后,new操作将永远不会执行了,因为获取到的实例不为null了
  3. 那么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. 申请内存空间
  2. 调用构造方法,初始化实例
  3. 把内存首地址赋给对象的引用

可以看出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.利用语法特性 三.实现方式&#xff08;饿汉式懒汉式&#xff09;1.饿汉式2.懒汉式3.线程安全的单例模式4.双重检查锁5.禁止指令重排序 一.什么是单例模式 单例模式&#xff08;Singleton Pattern&#xff…...

几种在JavaScript中创建对象的方式!

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 字面量方式⭐ 构造函数方式⭐ Object.create()方式⭐ 工厂函数方式⭐ ES6类方式⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门…...

java项目mysql转postgresql

特殊函数 &#xff1a; mysql&#xff1a; find_in_set(?, ancestors) postgresql&#xff1a; ? ANY (string_to_array(ancestors,,)) mysql&#xff1a; date_format(t1.oper_time, %Y-%m-%d) postgresql&#xff1a; rksj::date to_char(inDate,YYYY-MM-DD) mysql&am…...

SpringBoot Mybatis 多数据源 MySQL+Oracle

一、背景 在SpringBoot Mybatis 项目中&#xff0c;需要连接 多个数据源&#xff0c;连接多个数据库&#xff0c;需要连接一个MySQL数据库和一个Oracle数据库 二、依赖 pom.xml <dependencies><dependency><groupId>org.springframework.boot</groupId&…...

(笔记五)利用opencv进行图像几何转换

参考网站&#xff1a;https://docs.opencv.org/4.1.1/da/d6e/tutorial_py_geometric_transformations.html &#xff08;1&#xff09;读取原始图像和标记图像 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五、在实际业务中的应用六、完整的业务代码示例&#xff08;基于 Web 端&#xff09;七、总结 一、前言 在这篇文章中&#xff0c;…...

nowcoder NC236题 最大差值

目录 题目描述&#xff1a; 示例1 示例2 题干解析&#xff1a; 暴力求解&#xff1a; 代码展示&#xff1a; 优化&#xff1a; 代码展示&#xff1a; 题目跳转https://www.nowcoder.com/practice/a01abbdc52ba4d5f8777fb5dae91b204?tpId128&tqId33768&ru/exa…...

TCP/IP五层模型、封装和分用

1.网络通信基础2.协议分层OSI七层协议模型TCP/IP五层/四层协议模型【重点】 3. 封装&分用 1.网络通信基础 IP地址&#xff1a;表示计算机的位置&#xff0c;分源IP和目标IP&#xff1b;举个例子&#xff1a;买快递&#xff0c;商家从上海发货&#xff0c;上海就是源IP&…...

LeetCode 面试题 01.08. 零矩阵

文章目录 一、题目二、C# 题解 一、题目 编写一种算法&#xff0c;若M N矩阵中某个元素为0&#xff0c;则将其所在的行与列清零。 点击此处跳转题目。 示例 1&#xff1a; 输入&#xff1a; [ [1,1,1], [1,0,1], [1,1,1] ] 输出&#xff1a; [ [1,0,1], [0,0,0], [1,0,1] ] 示…...

Qt应用开发(基础篇)——进度条 QProgressBar

一、前言 QProgressBar类继承于QWidget&#xff0c;是一个提供了横向或者纵向进度条的小部件。 QProgressBar进度条一般用来显示用户某操作的进度&#xff0c;比如烧录、导入、导出、下发、上传、加载等这些需要耗时和分包的概念&#xff0c;让用户知道程序还在正常的执行中。 …...

108页石油石化5G智慧炼化厂整体方案PPT

导读:原文《108页石油石化5G智慧炼化厂整体方案PPT》(获取来源见文尾),本文精选其中精华及架构部分,逻辑清晰、内容完整,为快速形成售前方案提供参考。以下是部分内容,...

Codeforces 1625E2 括号树 + BIT

题意 传送门 Codeforces 1625E2 Cats on the Upgrade (hard version) 题解 首先利用栈将原始字符串转换为合法的 RBS&#xff0c;不能匹配的括号设为 ‘.’。根据匹配的括号序列构造树&#xff0c;具体而言&#xff0c;遇到左括号&#xff0c;则新建节点向下递归&#xff0c…...

PHP命令行CLI的使用

PHP命令行界面 PHP命令行界面&#xff08;CLI&#xff09;是一种使用命令行&#xff08;终端&#xff09;来运行PHP脚本的方式&#xff0c;与在Web服务器环境下运行PHP不同。CLI提供了一种与操作系统交互的方式&#xff0c;能够在命令行中直接执行PHP代码。 以下是一些与PHP命…...

近期嵌软线下笔试题记录

1、以下代码的输出结果是&#xff1f; #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主从同步&#xff08;一主两从&#xff09; 1.主库配置 2.建立同步账号 3.锁表设置只读 4.备份数据库数据 5.主库备份数据上传到从库 6.从库上还原备份 7.解锁 8.从库上设定主从同步 9.启动从库同步开关 10.检查状态 二.基于MySQL一主两从配置&…...

java八股文面试[多线程]——合适的线程数是多少

知识来源&#xff1a; 【并发与线程】 合适的线程数量是多少&#xff1f;CPU 核心数和线程数的关系&#xff1f;_哔哩哔哩_bilibili 【2023年面试】程序开多少线程合适_哔哩哔哩_bilibili...

Linux系统下vim常用命令

一、基础命令&#xff1a; v:可视模式 i:插入模式 esc:命令模式下 :q &#xff1a;退出 :wq &#xff1a;保存并退出 ZZ&#xff1a;保存并退出 :q! &#xff1a;不保存并强制退出二、在Esc下&#xff1a; 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实现...

智能井盖传感器,物联网智能井盖系统

随着城市人口的不断增加和城市化进程的不断推进&#xff0c;城市基础设施的安全和可靠性变得愈发重要&#xff0c;城市窨井盖作为城市基础设施重要组成部分之一&#xff0c;其安全性事关城市安全有序运行和居民生产生活安全保障。 近年来&#xff0c;各地都在加强城市窨井盖治理…...

C语言三子棋解析

目录&#xff08;标2的是我自己写的一堆问题不知道怎么改&#xff09; 开始菜单1打印棋盘1玩家下棋1电脑下棋1判断输赢1开始菜单2打印棋盘2选择先后2玩家下棋2电脑下棋2判断输赢2完整代码文件else.h文件else.c文件test.c 开始菜单1 void menu()//打印菜单 {printf("*****…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

基于当前项目通过npm包形式暴露公共组件

1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹&#xff0c;并新增内容 3.创建package文件夹...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

【生成模型】视频生成论文调研

工作清单 上游应用方向&#xff1a;控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...