【设计模式】创建型-单例模式
文章目录
- 一、单例模式
- 二、单例模式的八种实现方式
- 2.1、饿汉式(静态常量)
- 2.2、饿汉式(静态代码块)
- 2.3、懒汉式(线程不安全)
- 2.4、懒汉式(线程安全,同步方法)
- 2.5、双重检查
- 2.6、静态内部类
- 2.7、枚举
一、单例模式
单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。
单例模式有三个要点:
- 某个类只能有一个实例
- 它必须自行创建这个实例
- 它必须自行向整个系统提供这个实例。
二、单例模式的八种实现方式
2.1、饿汉式(静态常量)
/*饿汉式(静态常量)*/
public class Singleton1 {//创建一个私有构造器,不让其他类newprivate Singleton1(){}//创建一个静态常量public static final Singleton1 INSTANCE = new Singleton1();//实例方法,方法是静态是为了通过类名调用public static Singleton1 newInstance(){return INSTANCE;}public static void main(String[] args) {Singleton1 s1 = Singleton1.newInstance();Singleton1 s2 = Singleton1.newInstance();//比较两个实例是否相等 结果:trueSystem.out.println(s1==s2);}
}
优缺点:
- 优点:简单,类加载的时候就完成了实例化,避免了线程安全问题。
- 缺点:如果没用到这个实例,也会实例化,浪费了内存。
2.2、饿汉式(静态代码块)
/*饿汉式(静态代码块)*/
public class Singleton2 {//创建一个私有构造器,不让其他类newprivate Singleton2(){}//定义一个静态实例public static Singleton2 instance;//静态代码块中实例化对象static {instance= new Singleton2();}//提供一个公有静态方法,放回实例化对象public static Singleton2 newInstance(){return instance;}public static void main(String[] args) {Singleton2 s1 = Singleton2.newInstance();Singleton2 s2 = Singleton2.newInstance();//比较两个实例是否相等 结果:trueSystem.out.println(s1==s2);}
}
优缺点跟上面的静态常量一样
2.3、懒汉式(线程不安全)
/** 懒汉式* 实例是在使用的时候创建,但线程不安全,会创建多个对象* */
public class Singleton3 {//定义instance静态变量private static Singleton3 instance;private Singleton3(){}//初始化方法,实现懒加载,需要时才创建对象public static Singleton3 newInstance() throws InterruptedException {//没有实例,则创建对象if (instance == null){//让线程睡一下,创造多线程进入条件Thread.sleep(20);instance = new Singleton3();}//实例化过,直接返回return instance;}public static void main(String[] args) {for (int i = 0; i < 100; i++) {//创建多线程,实现Runnable接口,重写run方法new Thread(new Runnable() {@Overridepublic void run() {try {//通过哈希码,看对象是否一样System.out.println(Singleton3.newInstance().hashCode());} catch (InterruptedException e) {e.printStackTrace();}}}).start();}}
}
优缺点:
- 优点:起到了懒加载效果,需要时才创建对象,但只适合在单线程下使用。
- 缺点:在多线程情况下,一个线程 进入了if (instance == null)判断语句块,还未来得及往下执行,另一个线程又进来了,这时就产生了多个实例,造成线程不安全。
2.4、懒汉式(线程安全,同步方法)
/** 懒汉式(线程安全,加入同步方法)* */
public class Singleton4 {//定义instance静态变量private static Singleton4 instance;private Singleton4(){}//加入同步方法,保证只有一个线程进入public static synchronized Singleton4 newInstance() throws InterruptedException {//没有实例,则创建对象if (instance == null){//让线程睡一下,创造多线程进入条件Thread.sleep(20);instance = new Singleton4();}//实例化过,直接返回return instance;}public static void main(String[] args) {for (int i = 0; i < 100; i++) {//创建多线程,实现Runnable接口,重写run方法new Thread(new Runnable() {@Overridepublic void run() {try {//通过哈希码,看对象是否一样System.out.println(Singleton4.newInstance().hashCode());} catch (InterruptedException e) {e.printStackTrace();}}}).start();}}
}
方式一是一个实例,同时锁住了空判断和创建实例,线程安全。但是这就相当于全部锁住了,就跟同步方法的效果一样,线程安全但效率很低
方式二不是一个实例,线程不安全,原因是一个线程进入了空判断,还没往下执行,另一个线程来了,其中一个线程拿到锁,往下执行创建了实例,执行完释放锁后,另一个线程也往下执行了并创建对象,两者创建的对象并不一致。
2.5、双重检查
public class Singleton6 {private static Singleton6 instance;private Singleton6(){};public static Singleton6 newInstance() throws InterruptedException {//双重检查,是单例if (instance == null){//首先判断实例是否为空,空就上锁synchronized (Singleton6.class){//上锁后,如果上面new出了个对象,此时在这判断是否为空,不为空就直接返回了,确保了只有一个实例if (instance == null){Thread.sleep(20);instance = new Singleton6();}}}return instance;}public static void main(String[] args) {for (int i = 0; i < 100; i++) {new Thread(() -> {try {System.out.println(Singleton6.newInstance().hashCode());} catch (InterruptedException e) {e.printStackTrace();}}).start();}}
}
双重检查实际上就是在懒汉式(同步代码块)的内部再添加了一个判断,这样就保证线程安全

2.6、静态内部类
public class Singleton7 {private Singleton7() {}//静态内部类里实例化对象,在Singleton7加载的时候,SingletonInstance内部类不加载,只在实例的时候加载private static class SingletonInstance{//静态属性,实例化对象private static final Singleton7 INSTANCE = new Singleton7();}//提供一个静态的公有方法,返回SingletonInstance类的实例public static Singleton7 newInstance(){return SingletonInstance.INSTANCE;}public static void main(String[] args) {for (int i = 0; i < 100; i++) {new Thread(()->{System.out.println(Singleton7.newInstance().hashCode());}).start();}}
}
这种方式采用了类加载的机制来保证初始化实例时只有一个线程,线程安全。静态内部类在 Singleton7 类被加载时并不会立即实例化,而是在调用 newInstance方法的时候才会实例化静态内部类,通过SingletonInstance类调用实例,从而完成 Singleton 的实例化。类的静态属性只会在第一次加载类的时候初始化,JVM 帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
2.7、枚举
package com.s.singleton;
/*** 枚举*/
public enum Singleton8 {INSTANCE;public static void main(String[] args) {for (int i = 0; i < 100 ; i++) {new Thread(()->System.out.println(Singleton8.INSTANCE.hashCode())).start();}}
}
枚举实现是单例的,线程安全,不仅可以解决线程同步,还可以防止反序列化。《Effective Java》作者 Josh Bloch 提倡的方式。
相关文章:
【设计模式】创建型-单例模式
文章目录一、单例模式二、单例模式的八种实现方式2.1、饿汉式(静态常量)2.2、饿汉式(静态代码块)2.3、懒汉式(线程不安全)2.4、懒汉式(线程安全,同步方法)2.5、双重检查2…...
Python 练习 六
1、(最大数的出现)编写程序读取整数,找出它们中的最大值,然后计算它的出现次数。假设输入以数字0结束。假设你输入的是“352555 0";程序找出的最大数是5,而5的出现次数是4。(提示:维护两个变量max和 count。变量max存储的是当前最大数,而…...
「SQL面试题库」 No_22 员工奖金
🍅 1、专栏介绍 「SQL面试题库」是由 不是西红柿 发起,全员免费参与的SQL学习活动。我每天发布1道SQL面试真题,从简单到困难,涵盖所有SQL知识点,我敢保证只要做完这100道题,不仅能轻松搞定面试࿰…...
瞒不住了,Prefetch 就是一个大谎言
本文正在参加「金石计划」 Prefetch 是一个谎言 我们知道,现在的应用程序已经发展到可以拆分为多个 JavaScript包了,为了获得更好的用户体验,这些 bundle 包通常需要预获取,即 prefetch! 但是现在的prefetch 效果有多糟糕我想你…...
这个时候了,你还不会不知道JavaMail API吧
一、概述 1.1 简述 JavaMail API 顾名思义,提供给开发者处理电子邮件相关的编程接口,它是Sun发布的用来处理email的API,其提供独立于平台且与协议无关的框架来构建邮件和消息传递应用。JavaMail API 提供了一组抽象类,用于定义组…...
JavaScript var let区别
文章目录JavaScript var & let区别变量作用域变量提升变量重复声明全局对象属性for循环中的作用域JavaScript var & let区别 var和let都是用来声明变量的关键字。 变量作用域 var声明的变量作用域是函数作用域或全局作用域,而let声明的变量作用域是块级作…...
Thinkphp 6.0容器和依赖注入
本节课我们来学习一下依赖注入的用法,以及容器的用法。 一.依赖注入 1. 手册对依赖注入比较严谨的说明,具体如下: 依赖注入其实本质上是指对类的依赖通过构造器完成自动注入,例如在控制器架构方法和操作 方法中一旦对参…...
Type javax.servlet.http.HttpServletRequest not present
运行环境 Swagger 3.0.0、springboot 3.0.0 产生原因: Swagger 3.0.0不支持spring3.0.0 两个解决方案: 1.降低springboot版本为2.x 2.放弃Swagger,使用 springdoc-openapi-starter-webmvc-ui 第二种解决方案: <dependen…...
一键配置Ubuntu的OpenHarmony基础编译环境
一键配置Ubuntu的OpenHarmony基础编译环境 一、配置前说明 该更新源仅适用于Ubuntu以下系列 Ubuntu18.04 Ubuntu20.04 Ubuntu22.04 强烈推荐Ubuntu20.04,本人使用的一直都是Ubuntu20.04 wsl的配置参见 如果使用的window wsl安装,则关于wsl配置可参考&a…...
ASP网络求职招聘系统的设计与实现
本文主要介绍了ASP,数据库等相关知识,同时较为详尽的阐述了网络求职招聘系统的实现。本系统是使用基于HTML语言,嵌套JavaScript源代码的ASP编程技术来开发,并以IIS为服务平台实现网络求职招聘系统的构建。后台数据库选用的是ACCES…...
面试—C++《智能指针》常考点
目录 1.为什么需要智能指针 2. 内存泄漏 2.1 什么是内存泄漏,内存泄漏的危害 2.2 内存泄漏分类 2.3 如何检测内存泄漏 2.4如何避免内存泄漏 3.智能指针的使用及原理 3.3 std::auto_ptr 3.4 std::unique_ptr 3.5 std::shared_ptr 1.为什么需要智能指针 下…...
自动化测试方案编写思路
澄清问题: 目标:完成项目的自动化测试,设计一个方案,告诉领导打算怎么做?有哪些流程?花多长时间?需要哪些资源帮助?达到什么样的效果? 现状:需求分析-是个什么样的项目&a…...
【爬虫】案例04:某小说网多线程小说下载
时光轮回,冬去春来,转眼时间来到了2023年4月。天空沥沥淅淅下着小雨,逐渐拉上了幕布。此刻,正值魔都的下班高峰,从地铁站出来的女孩子纷纷躲到一边,手指飞快的划过手机屏幕,似乎在等待男朋友送来…...
海外独立站创业,Shopify网站如何引流
上一期给大家科普了如何快速创建自己的独立站 但往往独立站的难点在于站外引流 今天就给大家分享可以通过哪些渠道给独立站引流 - ⚡SEO排名:Google SEO的重要性不必多说,尽快注册歌账号,并开通Google Ad和Google Merchant Center&#…...
基于51单片机的室内湿度加湿温度声光报警智能自动控制装置设计
wx供重浩:创享日记 对话框发送:单片机湿度 获取完整无水印论文报告(内含电路原理图和源程序代码) 在日常生活中加湿器得到了广泛的应用,但是现有的加湿器都需要手工控制开启和关闭并且不具备对室内空气温湿度的监测&am…...
解决:github爆 WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
目录1. 背景2. 解决办法3. 原因,感兴趣的可以看看1. 背景 在拉取github上一个新项目的时候爆出 WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! 第一反应是电脑被黑了,传说中的中间人攻击(题外话一下,其实所有的代理软件都算是中间人哦~…...
【django开发手册】如何使用select_related进行一次连表查询
前言 Django是一款Python Web框架,致力于充分利用Python的简洁语法和语言特性来提高Web开发的效率。其中一个强大的特性是ORM(Object-Relational Mapping),它使开发者可以使用Python代码而不是SQL查询语言来访问数据库。ORM不仅使…...
二、MySQL 基础
二、MySQL 基础 2.1 MySQL 简介 MySQL 是一款流行的开源数据库,也是一个关系型数据库管理系统 在 WEB 应用方面 MySQL 是最好的 RDBMS(Relational Database Management System:关系数据库管理系统)应用软件之一 2.1.1 MySQL 发展历史 时间里程碑1996…...
项目中常用写法(前端)
项目中常用写法(前端)vue等待某个方法执行结束后,在执行判断js是不是undefined类型父组件传值到子组件state的值在标签中直接使用读取html,去掉字符串中的html标签字符串去掉中括号去掉双引号判断数组中是否包含某个值在某个ui框架…...
【面试】Java并发编程面试题
文章目录基础知识为什么要使用并发编程多线程应用场景并发编程有什么缺点并发编程三个必要因素是什么?在 Java 程序中怎么保证多线程的运行安全?并行和并发有什么区别?什么是多线程多线程的好处多线程的劣势:线程和进程区别什么是…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...
使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...
倒装芯片凸点成型工艺
UBM(Under Bump Metallization)与Bump(焊球)形成工艺流程。我们可以将整张流程图分为三大阶段来理解: 🔧 一、UBM(Under Bump Metallization)工艺流程(黄色区域ÿ…...
