【深入理解设计模式】单例设计模式

单例设计模式

在这里插入图片描述

概念:

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。

单例设计模式是一种创建型设计模式,其主要目的是确保类在应用程序中的一个实例只有一个。这意味着无论在应用程序的哪个位置请求该类的实例,都将获得同一个实例。这种模式通常用于控制某些共享资源的访问,或者在整个应用程序中管理唯一的状态。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

单例模式的实现:

单例设计模式分类两种:

  1. 饿汉式:类加载就会导致该单实例对象被创建
  2. 懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建
饿汉式实现方式:
饿汉式实现1 - 静态变量方式
/**
* @author OldGj 
* @version v1.0
* @apiNote 单例设计模式 - 饿汉式:静态变量实现
*/
public class Singleton {// 1.构造方法私有化private Singleton() {}// 2.在成员位置创建本类的对象private static final Singleton instance = new Singleton();// 3.对外界提供可以访问到自身创建的对象的静态方法public static Singleton getInstance(){return instance;}
}

注意:
该方式在成员位置声明Singleton类型的静态变量,并创建Singleton类的对象instance。instance对象是随着类的加载而创建的。如果该对象足够大的话,而一直没有使用就会造成内存的浪费。

饿汉式实现2 - 静态代码块方式

拓展:静态代码块在类加载过程的初始化阶段执行。
类的加载过程分为: 加载 -> 链接验证准备解析) -> 初始化

/*** @author OldGj * @version v1.0* @apiNote 单例设计模式 - 饿汉式:静态代码块实现*/
public class Singleton {// 1.构造器私有化private Singleton(){}// 2.声明静态全局变量private static Singleton instance;// 3.在静态代码块中进行赋值static {instance = new Singleton();}// 4.对外提供可以获取本类对象的静态方法public static Singleton getInstance(){return instance;}
}

注意:
该方式在成员位置声明Singleton类型的静态变量,而对象的创建是在静态代码块中,也是对着类的加载而创建。所以和饿汉式的方式1基本上一样,当然该方式也存在内存浪费问题。

饿汉式实现3 - 枚举

枚举类实现单例模式是极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式。

/**
* @author OldGj 
* @version v1.0
* @apiNote 单例模式 - 饿汉式 > 利用枚举实现 <
*/
public enum Singleton {INSTANCE;
}

注意:
枚举方式属于饿汉式方式。如果在不考虑饿汉式存在内存浪费问题的情况下使用枚举类型创建单例模式是最好的单例实现方式,因为枚举类型是天然的线程安全的,并且实现方式简单,只会被加载一起,而且是所有单例实现方式中,唯一一种不会被破坏的实现方式。

懒汉式实现方式:
懒汉式实现1 - 会有线程安全问题
/**
* @author OldGj
* @version v1.0
* @apiNote 单例设计模式 - 懒汉式 > 线程不安全 <
*/
public class Singleton {// 1.构造器私有化private Singleton() {}// 2.在成员位置创建本类的对象private static Singleton instance;// 3.对外提供可以获取到本类对象的静态方法public static Singleton getInstance() {// 只有在instance变量还没有实例化对象的时候,才实例化一个对象if (instance == null) {// 线程1 -> 等待// 线程2,分配到CPU,实例化了一个Singleton实例并赋值给instance变量// 线程1,因为已经进入判断,因此线程1又实例化了一个Singleton对象,不符合单例模式instance = new Singleton();}return instance;}
}

注意:
该从上面代码我们可以看出该方式在成员位置声明Singleton类型的静态变量,并没有进行对象的赋值操作,那么什么时候赋值的呢?当调用getInstance()方法获取Singleton类的对象的时候才创建Singleton类的对象,这样就实现了懒加载的效果。但是,如果是多线程环境,会出现线程安全问题。

懒汉式实现2 - synchronized同步锁解决线程安全问题
/*** @author OldGj * @version v1.0* @apiNote 单例设计模式 - 懒汉式  > 线程安全 <*/
public class Singleton {// 1.构造器私有化private Singleton (){}// 2.在成员位置声明Singleton类型的静态变量private static Singleton instance;// 3.对外提供一个可以获取本类对象的静态方法// 并且将该方法使用synchronized同步锁加锁,确保实例化过程中的线程安全public static synchronized Singleton getInstance(){if(instance==null){instance = new Singleton();}return instance;}
}

注意:
该方式也实现了懒加载效果,同时又解决了线程安全问题。但是在getInstance()方法上添加了synchronized关键字,导致该方法的执行效果特别低。从上面代码我们可以看出,其实就是在初始化instance的时候才会出现线程安全问题,一旦初始化完成就不存在了。

懒汉式实现3 - 双重检查锁

再来讨论一下懒汉模式中加锁的问题,对于 getInstance() 方法来说,绝大部分的操作都是读操作读操作是线程安全的,所以我们没必让每个线程必须持有锁才能调用该方法,我们需要调整加锁的时机。由此也产生了一种新的实现模式:双重检查锁模式


/*** @author OldGj * @version v1.0* @apiNote 单例设计模式 - 懒汉式 <b> > 双重检查锁模式 < <b/><br/>*          <p>多线程环境下,不会出现线程安全问题,也不会有性能问题*推荐使用*<p/>*/
public class Singleton {// 1.构造器私有化private Singleton(){}// 2.在成员位置声明Singleton类型的静态变量并用volatile关键字修饰private static volatile Singleton instance;// 3.对外提供可以获取本类对象的静态方法,并在方法内采用双重检查锁保证线程安全public static Singleton getInstance(){// 第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例if(instance==null){synchronized (Singleton.class){// 抢到锁之后再次判断是否为nullif(instance==null){instance = new Singleton();}}}return instance;}}

注意:
双重检查锁模式是一种非常好的单例实现模式,解决了单例、性能、线程安全问题,上面的双重检测锁模式看上去完美无缺,其实是存在问题,在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。

要解决双重检查锁模式带来空指针异常的问题,只需要使用 volatile 关键字, volatile 关键字可以保证可见性和有序性。

添加 volatile 关键字之后的双重检查锁模式是一种比较好的单例实现模式,能够保证在多线程的情况下线程安全也不会有性能问题。

懒汉式实现4 - 静态内部类方式

静态内部类单例模式中实例由内部类创建,由于 JVM 在加载外部类的过程中, 是不会加载静态内部类的, 只有内部类的属性/方法被调用时才会被加载, 并初始化其静态属性。静态属性由于被 static 修饰,保证只被实例化一次,并且严格保证实例化顺序。

/*** @author OldGj * @version v1.0* @apiNote 单例设计模式 - 懒汉式 > 静态内部类实现 <*/
public class Singleton {/*** 静态内部类单例模式中实例由内部类创建,* 由于 JVM 在加载外部类的过程中, 是不会加载静态内部类的, 只有内部类的属性/方法被调用时才会被加载,* 并初始化其静态属性。静态属性由于被 `static` 修饰,保证只被实例化一次,并且严格保证实例化顺序。*/// 1.构造器私有化private Singleton() {}// 2.创建静态内部类,并且在静态内部类中创建外部类的实例private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}// 3.对外提供可以获得本类实例的静态方法public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}

说明:

​ 第一次加载Singleton类时不会去初始化INSTANCE,只有第一次调用getInstance(),虚拟机加载SingletonHolder并初始化INSTANCE,这样不仅能确保线程安全,也能保证 Singleton 类的唯一性。

小结:

​ 静态内部类单例模式是一种优秀的单例模式,是开源项目中比较常用的一种单例模式。在没有加任何锁的情况下,保证了多线程下的安全,并且没有任何性能影响和空间的浪费。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/690502.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

单播、组播,广播介绍

单播、组播和广播是网络通信中的三种不同的数据传输方式&#xff0c;它们的主要区别在于数据传输的目标地址&#xff1a; 1. 单播&#xff08;Unicast&#xff09;&#xff1a;单播是指在网络中发送数据时&#xff0c;数据只会被发送到一个特定的目标地址。在单播传输中&#x…

【Vue2】element 穿梭框使用

<el-dialog :title"title" :visible.sync"showTransfer" width"650px" append-to-body><el-transfer :props"{key: stationId,label: stationName}":titles"[未关联电站, 已关联配电站]" :filterable"false&q…

IO 作业 24/2/19

1> 使用fread和fwrite完成两个文件的拷贝 #include <myhead.h> int main(int argc, const char *argv[]) {//定义被复制文件指针FILE *fp1NULL;if((fp1fopen("./111.bmp","r"))NULL){perror("error open");return -1;}//定义目标文件指…

Elasticsearch 与 OpenSearch:开源搜索技术的演进与选择

在2010年以Apache 2.0开源协议发布后&#xff0c;Elasticsearch迅速成为全球最受欢迎的企业搜索引擎。 Elasticsearch常与Logstash和Kibana一起部署&#xff0c;这一组合被称为 Elasitc Stack&#xff0c;用于启用日志分析用例&#xff0c;包括应用可观察性、安全日志分析和理解…

opencv计算机视觉

树莓派主机的无键盘解决 进入控制面板&#xff0c;更改适配器设置&#xff0c;WIFI属性&#xff0c;勾选 1.将网线两头分别接入树莓派和笔记本的网线接口 2.在无线连接属性那里勾选允许其他用户连接 3.运行cmd使用arp -a查看树莓派ip地址&#xff0c;或者使用ipscanner查看 cmd…

Springboot+vue的疫情信息管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue的疫情信息管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的前后端分离的疫情信息管理系统&#xff0c;采用M&#xff08;model&a…

计算机专业必看的几部电影

计算机专业必看的几部电影 计算机专业必看的几部电影&#xff0c;就像一场精彩的编程盛宴&#xff01;《黑客帝国》让你穿越虚拟世界&#xff0c;感受高科技的魅力&#xff1b;《社交网络》揭示了互联网巨头的创业之路&#xff0c;《源代码》带你穿越时间解救世界&#xff0c;…

LeetCode每日一题589. N-ary Tree Preorder Traversal

文章目录 一、题目二、题解 一、题目 Given the root of an n-ary tree, return the preorder traversal of its nodes’ values. Nary-Tree input serialization is represented in their level order traversal. Each group of children is separated by the null value (S…

【Git】基本概念和使用方式

Git是一个分布式版本控制系统&#xff0c;用于管理和跟踪计算机文件的变化。它最初是由Linus Torvalds开发的&#xff0c;用于管理Linux内核的开发。 Git的基本概念包括&#xff1a; 仓库&#xff08;Repository&#xff09;&#xff1a;一个仓库是Git中存储项目文件和历史变更…

webdriver.Remotegrid

在其他主机上运行webdriver,可用于分布式执行环境 1.下载和Chrome版本对应的webdriver,将其添加到PATH环境变量中 Linux下:export PATH=$PATH:Library/Drivers >> ~/.bash_profile source ~/.bash_profileMac下: Mac 下的source xxx只会生效一次(从 macOS Catalina …

如何结合《ISO 55001资产管理-管理系统要求》,提升资产管理绩效

在当今竞争激烈的商业环境中&#xff0c;有效的资产管理对于组织的成功至关重要。ISO 55001标准为组织提供了一个框架&#xff0c;帮助其建立和维护一个高效的资产管理系统&#xff0c;从而实现更好地管理资产并提升业绩的目标。本文将探讨如何结合ISO 55001标准&#xff0c;以…

猫多喝水好吗?可以促进猫咪多喝水的主食分享

猫咪多喝水确实是有益的。适量的饮水对于猫咪的健康至关重要&#xff0c;有助于维持体液平衡、促进消化、减少便秘的风险&#xff0c;并对泌尿系统的健康起到保护作用。正常情况下&#xff0c;建议每公斤体重的猫每天摄入60-80毫升的水&#xff0c;除了与体重相关外&#xff0c…

python使用工厂模式和策略模式实现读文件、分析内容功能

当涉及到在 Python 中创建类以及使用设计模式来实现读取文件和分析内容的功能时&#xff0c;我们可以考虑使用工厂模式和策略模式的结合。下面是一个简单的示例&#xff0c;演示如何通过创建类和使用设计模式来实现这一功能&#xff1a; # 工厂模式&#xff1a;根据不同的分析…

使用chatgpt写VBA程序操作EXCEL

在chatgpt输入&#xff0c;我有个EXCEL&#xff0c;它有4列&#xff0c;第一列是序号&#xff0c;第二列是机号&#xff0c;第三列是日期&#xff0c;第四列是能耗。同一机号会对应多个日期和多个该日期的能耗。我想让同一个机号的数据下新增加一行&#xff0c;在这一行内算出该…

基于51单片机--智能指纹识别密码锁设计(软件准备)

一.系统介绍 本次分享的是一款基于51单片机的智能指纹识别密码锁设计&#xff0c;该系统以STC89C52单片机作为模块核心&#xff0c;通过串口通信控制指纹模块AS608实现录取指纹并存储指纹数据&#xff0c;并通过LCD12864液晶显示屏比对流程及效果&#xff0c;采用4X4矩阵键盘完…

【Linux系统化学习】深入理解文件系统(Ext2文件系统)

目录 前言 磁盘的物理结构 物理结构 磁头和盘片工作解析图 盘面区域划分图&#xff08;俯视盘面图&#xff09; 扇区的寻址、定位&#xff08;CHS定位&#xff09; 磁盘存储的逻辑抽象结构 LBA定址 文件系统 磁盘分区 EXT2文件系统 组块中的信息介绍 查看inode编号…

14个常见的Java课程设计/毕业设计合集(源码+文档)

从网上整理收集了14个常见的java系统设计源码&#xff0c;可以用于课程作业或者毕业设计。 1.基于java的家政预约网站系统 平台采用B/S结构&#xff0c;后端采用主流的Springboot框架进行开发&#xff0c;前端采用主流的Vue.js进行开发。 整个平台包括前台和后台两个部分。 …

ComfyUI新宠,精准位置生成模型GLIGEN,附下载

GLIGEN 是一种在文本到图像模型中指定对象精确位置的直观方法。自带GUI&#xff0c;操作非常便利&#xff0c;可以精确控制要在什么位置画什么内容&#xff0c;比纯文字描述的RPG-DiffusionMaster更精确。 ComfyUI是一个基于节点的图形用户界面&#xff08;GUI&#xff09;工具…

【AI场景应用】智能电话机器人

从前有一个小型电商公司&#xff0c;每天都接收大量的客户咨询和订单确认电话。由于人手不足&#xff0c;公司的客服团队经常忙得不可开交。为了解决这个问题&#xff0c;他们引入了一位智能电话机器人&#xff0c;名叫小智。 小智是一位功能强大的机器人&#xff0c;他能够全…

助力智能化农田作物除草,基于轻量级YOLOv8n开发构建农田作物场景下玉米苗、杂草检测识别分析系统

在我们前面的系列博文中&#xff0c;关于田间作物场景下的作物、杂草检测已经有过相关的开发实践了&#xff0c;结合智能化的设备可以实现只能除草等操作&#xff0c;玉米作物场景下的杂草检测我们则少有涉及&#xff0c;这里本文的主要目的就是想要基于最新的YOLOv8下最轻量级…