【Java开发】设计模式 01:单例模式
1 单例模式介绍
单例模式(Singleton Pattern)是Java中最为基础的设计模式。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
1.1 场景
目的:控制实例数目,节省系统资源;
适用:若一个全局使用的类频繁地创建与销毁,单例模式可以证一个类仅有一个实例,并提供一个访问它的全局访问点。
要求生产唯一序列号;
WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来;
创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
优点:
如此内存中只有一个实例,减少了内存的开销;
避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
注:spring boot项目中将该对象注入到bean,那么该对象就默认为单例,这时也可以使用@Scope去设定单例和原型以及其他模式。
1.2基础实现方式
以下是基础的实现方式,创建一个 Singleton 类,存在私有构造函数和本身的一个静态实例。
Singleton 类提供了一个静态方法,供外界获取它的静态实例。Client,我们的演示类使用 Singleton 类来获取 Singleton 对象。
📌 1.创建Singleton单例类
public class Singleton {//让构造函数为 private,这样该类就不会被实例化private Singleton(){}//创建 SingleObject 的一个对象private static Singleton INSTANCE = new Singleton();//获取唯一可用的对象public static Singleton getInstance(){return INSTANCE;}}
📌 2.从singleton类获取唯一的对象。
public class Client {public static void main(String[] args) {//不合法的构造函数--因为SingleObject()私有,不可见
// Singleton singleton = new Singleton();Singleton singleton1 = Singleton.getInstance();Singleton singleton2 = Singleton.getInstance();System.out.println(singleton1);System.out.println(singleton2);}
}
控制台输出:
2 实现方式汇总
类型 | Lazy 初始化 | 多线程安全 | 实现难度 |
懒汉式--线程不安全 | 是 | 否 | 容易 |
懒汉式--线程安全 | 是 | 是 | 容易 |
饿汉式 | 否 | 是 | 容易 |
双重校验锁 | 是 | 是 | 较复杂 |
双重校验锁+volatile | 是 | 是 | 较复杂 |
静态内部类 | 是 | 是 | 一般 |
枚举 | 否 | 是 | 容易 |
ThreadLocal | 是 | 是 | 较复杂 |
CAS锁 | 是 | 是 | 较复杂 |
2.1 懒汉式--线程不安全
这种方式是最基本的实现方式,最大的问题是不支持多线程,因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
public class Lazyman1 {private static Lazyman1 instance;private Lazyman1(){}public static Lazyman1 getInstance() {if (instance == null){instance = new Lazyman1();}return instance;}
}
接下来介绍的几种实现方式都支持多线程,但是在性能上有所差异。
2.2 懒汉式--线程安全
这种方式能够在多线程中很好的工作,但是效率低。
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
public class Lazyman2 {private static Lazyman2 instance;private Lazyman2(){}public static synchronized Lazyman2 getInstance() {if (instance == null){instance = new Lazyman2();}return instance;}
}
2.3 饿汉式
这种方式比较常用,但容易产生垃圾对象。它基于类加载机制避免了多线程的同步问题,因为类加载过程JVM会自动加锁,因此保证了单例特性。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
public class Hungry {private Hungry(){}private final static Hungry HUNGRY = new Hungry();public static Hungry getInstance(){return HUNGRY;}
}
2.4 双重校验锁(DCL,即 double-checked locking)
这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
不过DCL还是可能会出现指令重排的问题~
public class DLC {private static DLC instance;private DLC(){}public static DLC getInstance() {if (instance==null){synchronized (LazyMan.class){if (instance==null){instance = new DLC();//不是一个原子性操作/** 1.分配内存空间* 2.执行构造方法,初始化对象* 3.把这个对象指向这个空间** 可能:* 123* 132 若多线程,A到达3,B会认为lazyMan非空,但是lazyMan此时还没有完成构造,那么就会有问题*/}}}return instance;}
}
2.5 双重校验锁+volatile
用于处理DCL可能出现的问题(指令重排)
解决方案:只需要给instance的声明加上volatile关键字(volatile--静止指令重排),对它的写操作就会有一个内存屏障。
public class DLCVolatile {private static volatile DLCVolatile instance;private DLCVolatile(){}public static DLCVolatile getInstance() {if (instance==null){synchronized (LazyMan.class){if (instance==null){instance = new DLCVolatile();}}}return instance;}
}
2.6 静态内部类
也称为登记类,这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
这种方式也利用了类加载机制保证初始化instance时只有一个线程,他和饿汉式不同在于:饿汉式只要单例类被加载,那么instance就会被实例化;而静态内部类类加载后,instance不一定被初始化,因为InnerClass类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载InnerClass类,从而实例化instance。
//静态内部类
public class Holder {private Holder(){}public static Holder getInstance(){return InnerClass.INSTANCE ;}public static class InnerClass{private static final Holder INSTANCE = new Holder();}
}
2.7 枚举
实现单例模式的最佳方法,简洁,自动支持序列化机制,防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,在实际工作中很少用。
📌 实现方式
public enum EnumSignle {INSTANCE;public EnumSignle getInstance(){return INSTANCE;}
}class Test{public static void main(String[] args) {EnumSignle enumSignle1 = EnumSignle.INSTANCE;EnumSignle enumSignle2 = EnumSignle.INSTANCE;System.out.println(enumSignle1);System.out.println(enumSignle2);}
}
控制台输出:
📌 通过枚举将已有类改造为单例类
public class Singleton {private Singleton(){}public static enum EnumSignle1 {INSTANCE;private Singleton instance = null;private EnumSignle1(){instance = new Singleton();}public Singleton getInstance(){return instance;}}
}class Test1{public static void main(String[] args) {Singleton singleton1 = Singleton.EnumSignle1.INSTANCE.getInstance();Singleton singleton2 = Singleton.EnumSignle1.INSTANCE.getInstance();System.out.println(singleton1 ==singleton2);}
}
2.8 ThreadLocal--线程安全
ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
public class TLSingleton {private static final ThreadLocal<TLSingleton> tlSingleton = new ThreadLocal<TLSingleton>(){@Overrideprotected TLSingleton initialValue() {return new TLSingleton();}};private TLSingleton(){}public static TLSingleton getInstance() {return tlSingleton.get();}
}
2.9 CAS锁实现(线程安全)
public class CASSingleton {private CASSingleton(){}private static final AtomicReference<CASSingleton> INSTANCE = new AtomicReference<CASSingleton>();public static CASSingleton getInstance() {for (; ; ) {CASSingleton current = INSTANCE.get();if (current != null) {return current;}current = new CASSingleton();if (INSTANCE.compareAndSet(null, current)) {return current;}}}
}
📌 总结
一般情况下,不建议使用第1种和第2种懒汉方式,建议使用第3种饿汉方式。只有在要明确实现lazy loading效果时,才会使用第6种静态内部类方式。如果涉及到反序列化创建对象时,可以尝试使用第7种枚举方式。如果有其他特殊的需求,可以考虑使用第4种双检锁方式。
相关文章:
【Java开发】设计模式 01:单例模式
1 单例模式介绍单例模式(Singleton Pattern)是Java中最为基础的设计模式。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对…...
10、go工程化与标准库
目录一、用go mod管理工程二、包引入规则三、init调用链四、可见性五、标准库1 - 时间函数2 - 数学计算3 - I/O操作4 - 编码一、用go mod管理工程 初始化项目:go mod init $module_name,$module_name和目录名可以不一样。上述命令会生成go.mod文件 mod…...
【Selenium自动化测试】鼠标与键盘操作
在 WebDriver 中,与鼠标操作相关的方法都封装在ActionChains 类中,与键盘操作相关的方法都封装在Keys类中。下面介绍下这两个类中的常用方法。 鼠标操作 ActionChains类鼠标操作常用方法: context_click():右击double_click()&…...
自定义javax.validation校验枚举类
枚举类单一情况 package com.archermind.cloud.phone.dto.portal.external.validation.validator;import com.archermind.cloud.phone.dto.portal.external.validation.constraints.EnumValidation; import lombok.extern.slf4j.Slf4j;import javax.validation.ConstraintVali…...
[Java·算法·中等]LeetCode39. 组合总和
每天一题,防止痴呆题目示例分析思路1题解1分析思路2题解2👉️ 力扣原文 题目 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形…...
【Linux】vi和vim编辑器
目录主题主题 三种常见模式: 正常模式 以vim 打开一个档案就直接进入一般模式了(这是默认的模式)。在这个模式中,你可以使用[上下左右]按键来移动光标,你可以使用『删除字符』或『删除整行』来处理档案内容,也可以使用「复制、…...
BIO,NIO,AIO
IO模型 用什么样的通道进行数据传输和接收,java支持3种io网络编程模式 BIO NIO AIO BIO 同步阻塞 一个客户端连接对应一个处理线程 BIO示例代码(客户端和服务端) package com.tuling.bio;import java.io.IOException; import java.net.So…...
代码随想录刷题-数组-有序数组的平方
文章目录有序数组的平方习题暴力排序双指针有序数组的平方 本节对应代码随想录中:代码随想录,讲解视频:有序数组的平方_哔哩哔哩_bilibili 习题 题目链接:977. 有序数组的平方 - 力扣(LeetCode) 给你一…...
【玩转c++】stack和queue的介绍和模拟实现
本期主题:list的讲解和模拟实现博客主页: 小峰同学分享小编的在Linux中学习到的知识和遇到的问题小编的能力有限,出现错误希望大家不吝赐stack的介绍和使用1.1.stack的介绍1. stack是一种容器适配器,专门用在具有后进先出操作的上…...
Linux order(文件、磁盘、网络、系统管理、备份压缩)
1. Linux 文件命令 -rwxrwxrwx chmod:change mode,用于(文件所有者或 root )变更用户(u:owner g:group o:other a:all)的权限 chmod [OPTION]… MODE[,MODE]… FILE… OPTION -R:递归修改more option:chmod…...
最详细的CentOS7安装Mysql数据库服务
1.查看是否安装mysql: rpm -qa | grep mysql如果有查出来东西,使用命令删除: rpm -e xxx2.检查是否有mysql用户组和mysql用户,没有就添加有就忽略: groups mysql 添加用户组和用户 groupadd mysql && useradd -r -g mysql mysql&a…...
【IoT】项目管理:如何做好端到端的项目管理?
今天主要来谈谈项目管理这个话题。 首先来看一个我在网络上看到的一个关于项目管理的案例或者是段子。 将项目管理的作用及意义非常直观地展示了出来。 有一个植树搞绿化的企业,在公司内部设置有五个部门,分别是: 运输部门;挖坑部…...
渲染十万条数据就把你难住了?不存在的!
虚拟列表的使用场景如果我想要在网页中放大量的列表项,纯渲染的话,对于浏览器性能将会是个极大的挑战,会造成滚动卡顿,整体体验非常不好,主要有以下问题:页面等待时间极长,用户体验差CPU计算能力…...
编程学习的心路历程和困惑回顾
回首入行9年的经历,从大一开始学习C语言和数据结构,老师一直是在用IDE演示程序的编写和运行,我们也就一直在跟黑乎乎的命令行窗口打交道。 后来在一些课程的实验环节,接触到了一些别人编写好的工程代码,知道了Makefile…...
请介绍类加载过程,什么是双亲委派模型?
第23讲 | 请介绍类加载过程,什么是双亲委派模型? Java 通过引入字节码和 JVM 机制,提供了强大的跨平台能力,理解 Java 的类加载机制是深入 Java 开发的必要条件,也是个面试考察热点。 今天我要问你的问题是࿰…...
Navisworks编辑材质和Revit快速切换材质问题
一、如何在Navisworks2016中编辑材质 初次使用NW2016-2017时发现,原来用于创建编辑材质的小地球不见了,如图1所示,在各大技术群里求助没有回应,度娘搜索也总是摇头。 经过仔细排查可能出现的地方,终于找到了可以编辑材…...
Object对象键值的输出循序到底如何排列的?
1.日常摸鱼看八股 今天又是复习八股文的一天,发现还是彻底懂得原理才好和面试官吹牛批呀。 接着来看看我chat大宝贝的回答: 在现代浏览器中,Object 对象的键值输出循序是比较稳定的,通常是按照如下顺序输出: 所有的数…...
气泡式水位计的安装方法详解
气泡水位计的安装实际上就是气管的安装,气管的安装是否正确将直接影响到仪器测量数据的结果,气泡水位计它由活塞泵产生的压缩空气流经测量管和气泡室,进入被测的水体中,测量管中的静压力与气泡室上的水位高度成正比。那么接下来就…...
求“二维随机变量的期望E(X)与方差D(X)”例题(一)
离散型 设随机变量(X,Y)的联合分布律为 X\Y0100.10.210.30.4 (1)求E(X) 先求x的边缘分布律,表格里x0的概率为0.10.2,于是我们可得 X01P0.30.7直接求E(X)即可,得到结果 (2)求E(XY) 直接x与y相乘就行。 记得别乘多了,别的算了又…...
MySQL 搞定行转列,列转行
行转列方法总结1、使用case…when…then2、使用SUM(IF()) 生成列3、使用SUM(IF()) 生成列 WITH ROLLUP 生成汇总行4、使用SUM(IF()) 生成列 UNION 生成汇总行,并利用 IFNULL将汇总行标题显示为 Total5、使用SUM(IF()) 生成列,直接生成汇总结果,不再利用…...
正点原子裸机开发之C语言点灯程序
一. 简介 本文针对 IMX6ULL 的裸机开发的(即不带Linux操作系统的开发)。 主要分两部分的工作: 1. 配置 C语言运行环境 2. C 语言编写及运行 二. 配置C语言运行环境 配置 C 语言运行环境的工作分 三部分。如下: 1. 设置…...
cv::阈值分割OTUS原理+代码
opencv库的阈值分割分为全局分割和局部分割全局分割:普通分割ret1,th1 cv2.threshold(img,127, 255, cv2.THRESH_BINARY) #127为阈值 #cv2.THRESH_BINARY |cv2.THRESH_BINARY_INV | cv2.THRESH_TRUNC|cv2.THRESH_TOZERO|cv2.THRESH_TOZERO_INV局部分割:…...
Postgresql-12.5 visual studio-2022 windows 添加pg工程并调试
pg内核学习,记录一下 文章目录安装包编译安装VS添加Postgresql工程调试源码安装包 (1)perl下载 https://www.perl.org/get.html (2)diff下载 http://gnuwin32.sourceforge.net/packages/diffutils.htm (…...
长沙学院2023 第一次蓝桥训练题解
每道题都在洛谷上,每个题都有很详细的题解,可以先自行做,不会再看题解。 题目解析思路都写在代码中,中文题面就不单独解释题意了。 P2440 木材加工(二分答案) 链接:P2440 木材加工 解析 代码…...
云端Docker搭建ABY库以及本地CLion使用
文章目录ABY的搭建以及使用前言ABY库的下载、安装及测试CLion配置后续杂项项目改名使用其他的库最后ABY的搭建以及使用 前言 仅做记录,仅供参考,不同人有不同的使用方式命令手敲,可能有错,自己辨识勿问,我懂的也不多…...
ES6-箭头函数、解构赋值、对象简写
箭头函数特点 1、 (只有1个形参) 可以省略() 2、 {} 可以省略 只有一句代码 或 只有返回值的时候,省略return 3、arguments 不可用,arguments在没有形参的时候可以拿到调用函数拿在的实参 获取伪数组通过Array.from转为真数组。 4、 箭头函数没有this, …...
【CSS】CSS 背景设置 ② ( 背景位置 | 背景位置-方位值设置 )
文章目录一、背景位置1、语法说明2、注意事项二、背景位置-方位值设置1、效果展示2、完整代码示例一、背景位置 1、语法说明 如果 盒子的大小 大于 背景图片的大小 , 默认的 图片 位置是 左上角 ; 设置背景位置的 CSS 语法如下 : background-position : length length backgro…...
HTML 扫盲
✏️作者:银河罐头 📋系列专栏:JavaEE 🌲“种一棵树最好的时间是十年前,其次是现在” 目录前言HTML 结构快速生成代码框架HTML 常见标签注释标签标题标签: h1-h6段落标签:p换行标签:br格式化标签…...
项目中用到的责任链模式
目录 1.什么是责任链?它的原理是什么? 2.应用场景 3.项目中的应用 传送门:策略模式,工作中你用上了吗? 1.什么是责任链?它的原理是什么? 将请求的发送和接收解耦,让多个接收对象…...
C++复习笔记--STL的string容器和vector容器
1--string容器string 本质上是一个类,其不同于指针 char*,string 类的内部封装了 char*,用于管理字符串,是一个 char* 型的容器;1-1--string构造函数string 的构造函数原型:string(); // 创建一个空的字符串…...
十大高端网站建设/微博关键词排名优化
通过 IPV4 连接一起正常,使用IPV6连接提示 failed to connect to ‘suroy.cn:port’: Connection refused 在stackoverflow找到相关问题 You will not be able to use IPV6 to connect to the adbd on the device until you upgrade it to Android 7.0 adbd in all…...
淘宝客网站做seo/优秀网站设计
代码里面写 if else 或者 switch case 语句,很常见,那么这2个写法除了姿势不一样以为,他们的效率是不是也差距比较大呢? 1,switch case 比 一个个if else快吗? 2,switch case会因为case的数据…...
柳州网站建设公/开一个免费网站
raspberry pi 常用的几个服务,ssh,vnc,bt配置 http://my.oschina.net/erasinoo/blog/112258...
软件开发可以做网站么/网络推广费用高吗
每篇一句 知其然,知其所以然才应该是做学问的态度。在应用时可以忽略非常多的实现细节,但在做学问时要吹毛求疵而不要放过任何一个细节 前言 众所周知,Spring容器可以简单粗暴的把它理解为一个大大的Map,存储着容器所管理的所有的…...
wordpress视差插件/百度搜索网站排名
学习java的时候,“设计模式”这个概念到处可见。比如java.io里面的 decorated pattern,Object.Clone(Object)原生态支持Prototype pattern,Swing事件响应的Observer pattern, io.util和Event中的Adapter pattern。还有第三方框架中形形色色的…...
网站备案 新增/网络服务提供者不是网络运营者
阅读该篇文章前,请大家事先阅读一下: java.toString(),(String),String.valueOf的区别 有了上述基础后,我接下来谈谈从一道题目中获得的些许收获。 今天在做题是发现了非常重要的一点。题目来源:http://www.lintcode.com/en/problem/anagra…...