Java多线程(七):单例模式详解

article/2025/9/11 18:07:41

目录

1. 什么是单例模式

2. 单例模式的特点

3. 单例模式的实现

3.1 单例模式实现步骤

3.2 单例模式实现方式

3.2.1 饿汉方式

3.2.2 懒汉方式


1. 什么是单例模式

        java中单例模式是一种常见的设计模式,单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。简而言之,单例模式能保证某个类在程序中只存在唯⼀⼀份实例, 而不会创建出多个实例。

2. 单例模式的特点

  1. 单例类只能有一个实例。
  2. 单例类必须自己创建自己的唯一实例。
  3. 单例类必须给所有其他对象提供这一实例。

3. 单例模式的实现

3.1 单例模式实现步骤

  1. 设置私有的构造函数;
  2. 声明⼀个私有的对象属性;
  3. 提供⼀个公共的获取实例的方法。

3.2 单例模式实现方式

单例模式具体的实现方式, 分成 "饿汉" 和 "懒汉" 两种:

  • 饿汉方式(线程安全)
  • 懒汉方式(使用时才加载,可以避免资源不必要的浪费)

3.2.1 饿汉方式

实现代码:

public class DataSourceSingleton {// 1.提供私有的构造方法(防止外部直接new对象)private DataSourceSingleton() {}// 2.创建一个私有的属性对象private static DataSourceSingleton dataSource = new DataSourceSingleton();// 3.提供公共对外的单例对象public static DataSourceSingleton getInstance() {return dataSource;}
}

3.2.2 懒汉方式

(1)单线程版本(非线程安全的)

public class DataSourceSingleton2 {// 1.提供私有的构造方法(防止外部直接new对象)private DataSourceSingleton2() {}// 2.创建一个私有的属性对象private static DataSourceSingleton2 dataSource = null;// 3.提供公共对外的单例对象public static DataSourceSingleton2 getInstance() {if (dataSource == null) {// 第一次访问try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}dataSource = new DataSourceSingleton2();}return dataSource;}
}

测试:

public class DataSourceTest {public static void main(String[] args) {Thread t1 = new Thread(() -> {System.out.println(DataSourceSingleton2.getInstance());});Thread t2 = new Thread(() -> {System.out.println(DataSourceSingleton2.getInstance());});Thread t3 = new Thread(() -> {System.out.println(DataSourceSingleton2.getInstance());});t1.start();t2.start();t3.start();}
}

 测试结果:

         可以看到产生了三个实例,而非一个,线程不安全。这种方式只能在单线程下使用。如果在多线程下,一个线程进入了if (dataSource == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。

(2)线程安全改进版一

public class DataSourceSingleton2 {// 1.提供私有的构造方法(防止外部直接new对象)private DataSourceSingleton2() {}// 2.创建一个私有的属性对象private static DataSourceSingleton2 dataSource = null;// 3.提供公共对外的单例对象public synchronized static DataSourceSingleton2 getInstance() {if (dataSource == null) {// 第一次访问try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}dataSource = new DataSourceSingleton2();}return dataSource;}
}

测试结果:

        可以看到只创建了一个实例。但是这种方式虽然线程安全,但是直接给全局加锁,效率太低了。不推荐使用。

 (3)线程安全改进版二

public class DataSourceSingleton3 {// 1.提供私有的构造方法(防止外部直接new对象)private DataSourceSingleton3() {}// 2.创建一个私有的属性对象private static volatile DataSourceSingleton3 dataSource = null;// 3.提供公共对外的单例对象public static DataSourceSingleton3 getInstance() {if (dataSource == null) {synchronized (DataSourceSingleton3.class) {dataSource = new DataSourceSingleton3();}}return dataSource;}
}

测试结果:

        可以看到这种方式也存在线程安全问题,假如一个线程进入了if (dataSource == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。不可用。

(4)线程安全改进版三

public class DataSourceSingleton4 {// 1.提供私有的构造方法(防止外部直接new对象)private DataSourceSingleton4() {}// 2.创建一个私有的属性对象private static DataSourceSingleton4 dataSource = null;// 3.提供公共对外的单例对象public static DataSourceSingleton4 getInstance() {if (dataSource == null) {synchronized (DataSourceSingleton3.class) {if (dataSource == null) {dataSource = new DataSourceSingleton4();}}}return dataSource;}
}

这种方式依然存在线程安全问题,对象创建需要三步:

  1. memory = allocate() //分配内存
  2. ctorInstanc(memory) //初始化对象
  3. instance = memory //设置instance指向刚分配的地址

假如因为指令重排导致执行的顺序变为了132,那么假如a线程中执行完13之后,b线程到达代码2处.执行判断语句.发现instance指向的是⼀段地址因此直接不进入判断语句而是直接返回了⼀个没有初始化的空的对象。也不可用。

(5)线程安全改进最终版

public class DataSourceSingleton5 {// 1.提供私有的构造方法(防止外部直接new对象)private DataSourceSingleton5() {}// 2.创建一个私有的属性对象private static volatile DataSourceSingleton5 dataSource = null;// 3.提供公共对外的单例对象public static DataSourceSingleton5 getInstance() {if (dataSource == null) {synchronized (DataSourceSingleton5.class) {if (dataSource == null) {dataSource = new DataSourceSingleton5();}}}return dataSource;}
}

双重检查,并且给对象加上了volatile,再进行了两次if (dataSource == null)检查,这样就可以保证线程安全了。这样,实例化代码只用执行一次,后面再次访问时,判断if (dataSource == null),直接return实例化对象。推荐使用。


http://chatgpt.dhexx.cn/article/n5lv3niA.shtml

相关文章

ARFoundation之路-3D物体识别之一

版权声明:Davidwang原创文章,严禁用于任何商业途径,授权后方可转载。 3D物体跟踪技术,是指通过图像处理技术对摄像头中拍摄到的3D物体识别定位并对其姿态进行跟踪的技术。3D物体跟踪技术的基础也是图像识别,但比前述图…

ARFoundation系列讲解 - 57 3D物体识别一

一、介绍 目前在ARFoundation中,3D物体识别只支持A9处理器或更高版本的iOS设备,系统版本要求iOS 12或更高版本,Android不支持此功能 。使用3D物体识别跟踪功能需要预先采集3D物体特征点信息。苹果公司给我们提供了一个采集3D物体特征点工具,我们需要下载并使用Xcode编译成应…

实战三:手把手教你实现物体识别

实战三:手把手教你实现物体识别 一、基于HaadAdaboost实现人脸识别 1.原理介绍(参考下面的博客文章) http://www.cnblogs.com/ello/archive/2012/04/28/2475419.html Haar分类器 Haar-like特征积分图方法AdaBoost级联 Haar分类器算法的…

手把手教你使用LabVIEW OpenCV dnn实现物体识别(Object Detection)含源码

文章目录 前言一、物体识别算法原理概述1、物体识别的概念2、Yolo算法原理概述 二、opencv调用darknet物体识别模型(yolov3/yolov4)1、darknet模型的获取2、python调用darknet模型实现物体识别3、LabVIEW调用darknet模型实现物体识别yolo_example.vi4、L…

颜色和移动物体识别系统

颜色和移动物体识别系统 1.开发工具 Python版本:Anaconda 的python环境3.8版本 开发软件:Pycharm社区版 识别模型:深度学习模型,普通学习模型 相关模块:opencv-python3.4.8.29模块 2.环境搭建 安装Anaconda并将路径添加…

[Vuforia]二.3D物体识别

之前说过10之后的版本没有3dScan了,所以还是9.8的版本或者之前更早的版本。 3d物体扫描需要先下载扫描的APK进行扫面。 首先要在手机上装一个扫描程序,扫描现实中的三维物体,然后上传高通官网,在下载成UnityPackage类型让Unity能…

K210视觉体验—物体识别

K210视觉体验—物体识别 使用设备ZTFR开发板 人脸识别构造函数导入模型 示例代码基础测试 使用设备 ZTFR开发板 人脸识别 首先简单介绍一下 K210 的 KPU。KPU 是 K210 内部一个神经网络处理器,简单来说就是 KPU 能加载和运行各种现成的 AI 算法模型,实现…

tensorflow 物体识别

tensorflow推出的object detection模块,为我们做物体检测提供了极大的便利,如果还没有安装该模块,可以参照 http://blog.csdn.net/hanshuobest/article/details/79222685 下面的示例演示如何用已训练的模型做物体检测 import numpy as np …

计算机视觉物体识别的过程,计算机视觉之从OpenCV到物体识别(二)

前言:上一章节我们学会了图片、视频以及外设获取帧图片的方式,以及OpenCV 的灰度图片处理。计算机不像人眼可以很轻易地辨别物体,本质是将一张图片转换成灰度图,通过一定的模型训练,使得机器能够从0,1(黑与白)中分辨物体的特征,由局部到整体,图片越复杂,所需的训练程度…

unity c# 触摸屏物体识别桌算法

unity源码地址链接:unityc#触摸屏物体识别桌算法-C#文档类资源-CSDN下载 实操的时候出现算出来的坐标不对,没有时间去找问题,优化了,所以代码仅供参考。 使用的是Lean Touch插件,免费的。 模块由三个点构成&#xf…

如何用AR引擎技术, 5步优雅实现物体识别和跟踪

AR技术让应用实现虚拟世界和现实世界的融合,让开发者的创意和脑洞能够拥有更多炫酷有趣的呈现方式。AR拍照、AR购物、AR教育、AR搜索等丰富应用场景,凭借着AR技术的加持,为用户带来更优质的沉浸式体验,为用户创造更多价值。 例如…

物体识别全流程(Ubuntu16.04)结合ROS

物体识别全流程(Ubuntu16.04)结合ROS 1.使用labellmg,标记图片,生成xml标签 在此下载labellmg包 下载之后解压到要放置的目录 推荐使用Python3Qt5 打开labellmg包 在当前目录终端下运行如下命令行 sudo apt-get install pyqt5-…

opencv人脸识别以及自定义物体识别系统

目录 一、简介: 二、软硬件应用: 三、模块介绍: (一)人脸识别: (二)、自定义物体识别: (三)、图片展示: (四&#x…

opencv物体识别-识别水果

前言 玩一玩用opencv做一些简单的物体识别 1.思路讲解 我们基于简单的opencv的阈值分割,通过这个阈值分割,我们能把我们需要识别的物体在二值图里面变成白色,其余的变成黑色。然后对我们分割出来的物体部分提取轮廓,算出覆盖轮…

物体识别

一、物体识别 图像识别:很重要,是很多算法的基础 图像识别定位:识别出来是猫,且定位出猫的位置 物体检测:每个图像中含有多物体,检测出有几个个体,并框出来位置 图像分割:不仅仅框…

【AI技术】物体识别概述1

【AI技术】物体识别概述1 1、背景2、物体识别3、应用 1、背景 主要针对客户以及初学者概述物体识别。 2、物体识别 物体识别又叫目标识别,物体分类,图像分类,习惯性称为图像分类,即对整张图片进行分类。 物体识别针对的是物体分…

目标检测和物体识别的方法有哪些?如何进行物体定位和分类?

目标检测和物体识别是计算机视觉领域中重要的任务,旨在从图像或视频中自动定位和识别出不同类别的物体。这些任务在许多应用领域中具有广泛的应用,如自动驾驶、视频监控、人机交互等。本文将介绍目标检测和物体识别的方法以及如何进行物体定位和分类。 一…

小程序 跳转 公众号 文章

1.通过组件 web-view https://developers.weixin.qq.com/miniprogram/dev/component/web-view.html <web-view src"https://mp.weixin.qq.com/s/Cz4x4QpvF_Pozn7xjYOVyw"></web-view>2.如果是关联好小程序的公众号 1.如果是 左图文章 都是 https://m…

微信小程序跳转公众号h5页面

小程序提供了web- view组件进行用来内嵌网页 根据限制说明准备域名即可&#xff0c;开发阶断可以使用http的域名&#xff0c;在开发者工具中勾选不验证https证书即可&#xff0c;发布上线时必须使用https的域名。 同时需要小程序后台配置业务域名。

微信小程序跳转微信公众号

1 webview 嵌套 微信公众号的页面 然后 文章里 添加 公众号二维码 然后引导用户长按识别公众号 2 公众号有关注的页面 路径 需要自己找 我记得是替换什么officeid啥的 百度即可 3 利用<official-account bindload"lookSuccess" binderror"lookFai…