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

【Java多线程】单例模式(饿汉模式和懒汉模式)

目录

单例模式的定义:

饿汉式--单例模式

 定义:

案例: 

优缺点: 

懒汉式--单例模式:

定义:

1)懒汉式单例模式(非线程安全) 

2)线程安全的懒汉式单例模式 (synchronized )

3)双重检查锁定的懒汉式单例模式(线程安全) 


单例模式的定义:

  • 单例模式是一种设计模式,它确保一个类只有一个实例并提供一个全局访问点来访问这个实例。就好像在一个软件系统中,对于某些特定的资源或者对象,只需要一个就足够了,例如数据库连接池、配置文件管理器等。通过单例模式可以更好地控制这些对象的创建和访问,避免创建多个实例导致资源浪费或者数据不一致等问题。

单例模式能保证某个类在程序中只存在唯⼀⼀份实例,⽽不会创建出多个实例. 

要实现单例模式,通常需要做到以下几点

  1. 私有化构造函数,防止外部通过new关键字创建实例。
  2. 提供一个静态的私有变量来保存类的唯一实例。
  3. 提供一个公共的静态方法来获取类的唯一实例,如果实例不存在则创建它。

单例模式具体的实现⽅式有很多.最常⻅的是"饿汉"和"懒汉"两种.


饿汉式--单例模式

定义:

  • 在饿汉式单例模式中,“饿” 体现的是一种急切的状态。就好像一个很饿的人,在看到食物(这里类比于单例对象)的时候,会迫不及待地先把食物拿到手(创建单例对象)。在这个模式下,单例对象在类加载阶段就被创建出来,而不是等到真正需要使用这个对象的时候才去创建。这种方式比较急切,所以被称为 饿汉模式”。

案例: 

class Singleton {// 私有静态成员变量,在类加载时就初始化实例private static Singleton instance = new Singleton();// 私有构造函数,防止外部通过new关键字创建实例private Singleton() {}// 公共静态方法,用于获取单例实例public static Singleton getInstance() {return instance;}
}
- ** 类加载过程中的创建**
     - 在Java中,类加载是由类加载器(ClassLoader)完成的一个过程。当一个类被首次主动使用(例如创建这个类的实例、访问这个类的静态成员等情况)时,这个类就会被加载。对于上述的`Singleton`类,当`Singleton`类被加载时,`private static Singleton instance = new Singleton();`这行代码就会被执行。 因为类加载机制保证了一个类在一个Java程序中只会被加载一次(在正常情况下),所以`instance`对象也只会被创建一次
   - **访问控制保证单例性**
     - 构造函数`private Singleton()`是私有的。这是非常关键的一点,它防止了外部类通过`new`关键字来创建`Singleton`类的新实例。外部类只能通过`public static Singleton getInstance()`方法来获取单例对象,而这个方法每次返回的都是在类加载阶段就已经创建好的`instance`对象,从而保证了整个系统中 只有一个`Singleton`类的实例存在。

优缺点: 

**优点**
   1 **线程安全**
     - 由于单例对象是在类加载阶段就创建好的,而类加载过程在Java中是线程安全的(由Java虚拟机来保证)。所以在多线程环境下,这种方式可以保证多个线程访问`getInstance`方法时,获取到的都是同一个单例对象,不会出现多个线程创建多个实例的情况。
   2- **实现简单**
     - 从代码量和逻辑复杂度来看,饿汉式单例模式是比较简单的。只需要在类中定义一个私有静态变量并初始化,再提供一个公共静态方法来返回这个变量即可。这种简单的实现方式使得代码易于理解和维护。
**缺点**
   - **可能会造成资源浪费**
     - 如果单例对象的创建过程比较复杂,例如需要进行大量的初始化操作,如加载配置文件、建立网络连接等,并且这个单例对象在程序运行初期可能并不一定需要被使用。那么在类加载阶段就创建这个单例对象可能会导致资源的浪费。就好像提前准备了一顿丰盛的大餐(单例对象),但可能很长时间都没有人来吃(使用单例对象),而准备这顿大餐(创建单例对象)的过程又耗费了很多资源。


懒汉式--单例模式:

定义:

 在懒汉模式下,实例在第一次使用时才进行创建,因此称为“懒汉”,在需要被用的时候被创建,突出一个字“

1)懒汉式单例模式(非线程安全) 

public class LazySingleton {// 私有静态变量,用于存储单例对象private static LazySingleton instance;// 私有构造函数,防止外部通过new关键字创建新的实例private LazySingleton() {}// 公共的静态方法,用于获取单例对象public static LazySingleton getInstance() {if (instance == null) {// 如果实例还未创建,则创建一个新的实例instance = new LazySingleton();}return instance;}
}
  • 这种实现方式在单线程环境下是可以正常工作的。当第一次调用getInstance方法时,会检查instance是否为null。如果是null,就会创建一个LazySingleton类的实例并赋值给instance,然后返回这个实例。之后再调用getInstance方法时,因为instance已经不是null了,所以会直接返回已创建的实例。
  • 存在的问题
    • 在多线程环境下,这种实现方式是不安全的。假设两个线程同时调用getInstance方法,并且此时instancenull。这两个线程都会执行instance = new LazySingleton();这一行代码,从而创建出两个不同的LazySingleton实例,这就违背了单例模式的初衷。

2)线程安全的懒汉式单例模式 (synchronized )

public class ThreadSafeLazySingleton {private static ThreadSafeLazySingleton instance;private ThreadSafeLazySingleton() {}// 使用synchronized关键字修饰方法,保证在多线程环境下的线程安全public static synchronized ThreadSafeLazySingleton getInstance() {if (instance == null) {instance = new ThreadSafeLazySingleton();}return instance;}
}
  • 通过在getInstance方法上添加synchronized关键字,保证了在多线程环境下,同一时刻只有一个线程能够进入这个方法。当一个线程进入getInstance方法并发现instancenull时,它会创建一个新的实例。其他线程如果在这个时候也尝试调用getInstance方法,就会被阻塞,直到第一个线程完成实例的创建并返回。
  • 存在的问题
    • 这种方式虽然保证了线程安全,但是性能较差。因为每次调用getInstance方法都需要获取锁,即使实例已经创建完成,这种不必要的同步操作会在高并发场景下成为性能瓶颈。

3)双重检查锁定的懒汉式单例模式(线程安全) 

public class DoubleCheckedLockingSingleton {// 使用volatile关键字保证变量的可见性和禁止指令重排序private static volatile DoubleCheckedLockingSingleton instance;private DoubleCheckedLockingSingleton() {}public static DoubleCheckedLockingSingleton getInstance() {if (instance == null) {// 第一次检查,提高性能,避免不必要的同步操作synchronized (DoubleCheckedLockingSingleton.class) {if (instance == null) {// 第二次检查,确保在同步块内也不会创建多个实例instance = new DoubleCheckedLockingSingleton();}}}return instance;}
}
  • 首先,if (instance == null)这一检查在同步块外进行,这是第一次检查。如果instance已经不是null就可以直接返回实例,避免了进入同步块,从而提高了性能
  • 当第一次检查instancenull时,线程会进入同步块。在同步块内,又进行了一次if (instance == null)检查,这是第二次检查。这是为了防止在多个线程同时通过第一次检查后,只有一个线程能够进入同步块创建实例,其他线程在等待这个线程完成创建后,直接获取已创建的实例,而不会再次创建
  • 使用volatile关键字是非常关键的。在 Java 中,指令重排序可能会导致instance变量在没有完全初始化的情况下就被其他线程看到。volatile关键字可以保证变量的可见性,并且禁止指令重排序,确保了单例模式的正确性。

结语: 写博客不仅仅是为了分享学习经历,同时这也有利于我巩固知识点,总结该知识点,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进。同时也希望读者们不吝啬你们的点赞+收藏+关注,你们的鼓励是我创作的最大动力! 

相关文章:

【Java多线程】单例模式(饿汉模式和懒汉模式)

目录 单例模式的定义: 饿汉式--单例模式 定义: 案例: 优缺点: 懒汉式--单例模式: 定义: 1)懒汉式单例模式(非线程安全) 2)线程安全的懒汉式单例模…...

python 异步编程之协程

最近在学习python的异步编程,这里就简单记录一下,免得日后忘记。 首先,python异步实现大概有三种方式,多进程,多线程和协程;多线程和多进程就不用多说了,基本上每种语言都会有多进行和多线程的…...

现代密码学|古典密码学例题讲解|AES数学基础(GF(2^8)有限域上的运算问题)| AES加密算法

文章目录 古典密码凯撒密码和移位变换仿射变换例题多表代换例题 AES数学基础(GF(2^8)有限域上的运算问题)多项式表示法 | 加法 | 乘法X乘法模x的四次方1的乘法 AES加密算法初始变换字节代换行移位列混合轮密钥加子密钥&#xff08…...

算法沉淀一:双指针

目录 前言: 双指针介绍 对撞指针 快慢指针 题目练习 1.移动零 2.复写零 3.快乐数 4.盛水最多的容器 5.有效三角形的个数 6.和为s的两个数 7.三数之和 8.四数之和 前言: 此章节介绍一些算法,主要从leetcode上的题来讲解&#xff…...

Word_小问题解决_1

1.第二页是空白的,但是删不掉 将鼠标弄到第二页最开始的地方打开段落设置行距为固定值0.7磅 2.表格中有文字进入了表格中怎么办 打开段落,将缩进改为0即可...

基于opencv制作GUI界面

可以基于cvui头文件实现一些控件操作&#xff0c;头文件及demo实例 这是一个demo main.cpp #include <opencv2/opencv.hpp> #define CVUI_IMPLEMENTATION #include "cvui.h"#define WINDOW_NAME "CVUI Hello World!"int main(void) {cv::Mat frame…...

微服务即时通讯系统的实现(客户端)----(2)

目录 1. 将protobuf引入项目当中2. 前后端交互接口定义2.1 核心PB类2.2 HTTP接口定义2.3 websocket接口定义 3. 核心数据结构和PB之间的转换4. 设计数据中心DataCenter类5. 网络通信5.1 定义NetClient类5.2 引入HTTP5.3 引入websocket 6. 小结7. 搭建测试服务器7.1 创建项目7.2…...

QT使用libssh2库实现sftp文件传输

本篇文章通过用户名和密码来连接服务器端,通过密匙连接服务器端可以参考另外一篇文章: https://blog.csdn.net/u012372584/article/details/143826199?sharetype=blogdetail&sharerId=143826199&sharerefer=PC&sharesource=u012372584&spm=1011.2480.3001.…...

【Linux】进程的优先级

进程的优先级 一.概念二.修改优先级的方法三.进程切换的大致原理&#xff1a;四.上下文数据的保存位置&#xff1a; 一.概念 cpu资源分配的先后顺序&#xff0c;就是指进程的优先权&#xff08;priority&#xff09;。 优先权高的进程有优先执行权利。配置进程优先权对多任务环…...

python实现十进制转换二进制,tkinter界面

目录 需求 效果 代码实现 代码解释 需求 python实现十进制转换二进制 效果 代码实现 import tkinter as tk from tkinter import messageboxdef convert_to_binary():try:# 获取输入框中的十进制数decimal_number int(entry.get())# 转换为二进制binary_number bin(de…...

电子应用设计方案-12:智能窗帘系统方案设计

一、系统概述 本设计方案旨在打造便捷、高效的全自动智能窗帘系统。 二、硬件选择 1. 电机&#xff1a;选用低噪音、扭矩合适的智能电机&#xff0c;根据窗帘尺寸和重量确定电机功率&#xff0c;确保能平稳拉动窗帘。 2. 轨道&#xff1a;选择坚固、顺滑的铝合金轨道&…...

力扣 回文链表-234

回文链表-234 const int N 1e55; int a[N];//定义一个整形的全局数组作为辅助数组存储链表反转前的值 class Solution { /*本题的解题思路是先将链表中每个值存储到辅助数组a中&#xff0c;然后反转链表&#xff0c; 最后&#xff0c;反转后链表的值和没反转之前的值&#xf…...

采样率22050,那么CHUNK_SIZE 一次传输的音频数据大小设置多少合适?unity接收后出现卡顿的问题的思路

在采样率为22050的情况下&#xff0c;选择合适的 CHUNK_SIZE 主要取决于 Unity 接收和处理音频数据的效率。以下是设置 CHUNK_SIZE 的一些建议&#xff1a; 计算 CHUNK_SIZE&#xff1a;音频的传输数据量可以通过公式 CHUNK_SIZE 采样率 * 传输间隔秒数 * 每样本字节数 * 声道…...

网络初识--Java

一、网络通信基础 1.IP地址 IP地址主要⽤于标识⽹络主机、其他⽹络设备&#xff08;如路由器&#xff09;的⽹络地址。简单说&#xff0c;IP地址⽤于定位主 机的⽹络地址。 就像我们发送快递⼀样&#xff0c;需要知道对⽅的收货地址&#xff0c;快递员才能将包裹送到⽬的地。…...

K8S单节点部署及集群部署

1.Minikube搭建单节点K8S 前置条件&#xff1a;安装docker&#xff0c;注意版本兼容问题 # 配置docker源 wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo# 安装docker环境依赖 yum install -y yum-utils device-m…...

GPIO相关的寄存器(重要)

目录 一、GPIO相关寄存器概述 二、整体介绍 三、详细介绍 1、端口配置低寄存器&#xff08;GPIOx_CRL&#xff09;&#xff08;xA...E&#xff09; 2、端口配置高寄存器&#xff08;GPIOx_CRH&#xff09;&#xff08;xA...E&#xff09; 3、端口输入数据寄存器&#xff…...

OpenCV基础

1. 基础入门&#xff1a;OpenCV概念与安装 a. OpenCV简介 OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开源的计算机视觉库&#xff0c;广泛应用于图像和视频处理、计算机视觉、机器学习等领域。 b. 安装OpenCV Python安装&#xff1a; pip in…...

两行命令搭建深度学习环境(Docker/torch2.5.1+cu118/命令行美化+插件),含完整的 Docker 安装步骤

深度学习环境的配置过于繁琐&#xff0c;所以我制作了两个基础的镜像&#xff0c;希望可以帮助大家节省时间&#xff0c;你可以选择其中一种进行安装&#xff0c;版本说明&#xff1a; base 版本基于 pytorch/pytorch:2.5.1-cuda11.8-cudnn9-devel&#xff0c;默认 python 版本…...

Redis做分布式锁

&#xff08;一&#xff09;为什么要有分布式锁以及本质 在一个分布式的系统中&#xff0c;会涉及到多个客户端访问同一个公共资源的问题&#xff0c;这时候我们就需要通过锁来做互斥控制&#xff0c;来避免类似于线程安全的问题 因为我们学过的sychronized只能对线程加锁&…...

lambdaQueryWrapper详细解释

LambdaQueryWrapper 是 MyBatis Plus 提供的一个强大的查询条件构建工具&#xff0c;它允许你使用 Lambda 表达式来构建查询条件&#xff0c;从而使代码更加简洁和易读。下面详细介绍 LambdaQueryWrapper 的使用方法及其底层原理。 什么是 LambdaQueryWrapper&#xff1f; La…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

蓝桥杯 冶炼金属

原题目链接 &#x1f527; 冶炼金属转换率推测题解 &#x1f4dc; 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V&#xff0c;是一个正整数&#xff0c;表示每 V V V 个普通金属 O O O 可以冶炼出 …...