重学 Java 设计模式:实战单例模式

作者:小傅哥
博客:https://bugstack.cn
沉淀、分享、成长,让自己和他人都能有所收获!
一、前言
5个创建型模式的最后一个
在设计模式中按照不同的处理方式共包含三大类;创建型模式、结构型模式和行为模式,其中创建型模式目前已经介绍了其中的四个;工厂方法模式、抽象工厂模式、生成器模式和原型模式,除此之外还有最后一个单例模式。
掌握了的知识才是自己的
在本次编写的重学 Java 设计模式的编写中尽可能多的用各种场景案例还介绍设计的使用,包括我们已经使用过的场景;各种类型奖品发放、多套Redis缓存集群升级、装修公司报价清单和百份考卷题目与答案乱序,通过这些场景案例的实践感受设计模式的思想。但这些场景都是作者通过经验分离出来的,还并不是读者的知识,所以你如果希望可以融会贯通的掌握那么一定要亲力亲为的操作,事必躬亲的完成。
书不是看的是用的
在这里还是想强调一下学习方法,总有很多小伙伴对学习知识有疑惑,明明看了、看的时候也懂了,但到了实际使用的时候却用不上。或者有时候在想是不要是有更加生动的漫画或者什么对比会好些,当然这些方式可能会加快一个新人对知识的理解速度。但只要你把学习视频当电影看、学习书籍当故事看,就很难掌握这项技术栈。只有你把它用起来,逐字逐句的深挖,一点点的探求,把各项遇到的盲点全部扫清,才能让你真的掌握这项技能。
二、开发环境
- JDK 1.8
- Idea + Maven
- 涉及工程1个,可以通过关注公众号:
bugstack虫洞栈,回复源码下载获取(打开获取的链接,找到序号18)
三、单例模式介绍
单例模式可以说是整个设计中最简单的模式之一,而且这种方式即使在没有看设计模式相关资料也会常用在编码开发中。
因为在编程开发中经常会遇到这样一种场景,那就是需要保证一个类只有一个实例哪怕多线程同时访问,并需要提供一个全局访问此实例的点。
综上以及我们平常的开发中,可以总结一条经验,单例模式主要解决的是,一个全局使用的类频繁的创建和消费,从而提升提升整体的代码的性能。
四、案例场景
本章节的技术所出现的场景非常简单也是我们日常开发所能见到的,例如;
- 数据库的连接池不会反复创建
- spring中一个单例模式bean的生成和使用
- 在我们平常的代码中需要设置全局的的一些属性保存
在我们的日常开发中大致上会出现如上这些场景中使用到单例模式,虽然单例模式并不复杂但是使用面却比较广。
五、7种单例模式实现
单例模式的实现方式比较多,主要在实现上是否支持懒汉模式、是否线程安全中运用各项技巧。当然也有一些场景不需要考虑懒加载也就是懒汉模式的情况,会直接使用static静态类或属性和方法的方式进行处理,供外部调用。
那么接下来我们就通过实现不同方式的实现进行讲解单例模式。
0. 静态类使用
public class Singleton_00 {
public static Map<String,String> cache = new ConcurrentHashMap<String, String>();
}
- 以上这种方式在我们平常的业务开发中非常场常见,这样静态类的方式可以在第一次运行的时候直接初始化Map类,同时这里我们也不需要到延迟加载在使用。
- 在不需要维持任何状态下,仅仅用于全局访问,这个使用使用静态类的方式更加方便。
- 但如果需要被继承以及需要维持一些特定状态的情况下,就适合使用单例模式。
1. 懒汉模式(线程不安全)
public class Singleton_01 {
private static Singleton_01 instance;
private Singleton_01() {
}
public static Singleton_01 getInstance(){
if (null != instance) return instance;
return new Singleton_01();
}
}
- 单例模式有一个特点就是不允许外部直接创建,也就是
new Singleton_01(),因此这里在默认的构造函数上添加了私有属性private。 - 目前此种方式的单例确实满足了懒加载,但是如果有多个访问者同时去获取对象实例你可以想象成一堆人在抢厕所,就会造成多个同样的实例并存,从而没有达到单例的要求。
2. 懒汉模式(线程安全)
public class Singleton_02 {
private static Singleton_02 instance;
private Singleton_02() {
}
public static synchronized Singleton_02 getInstance(){
if (null != instance) return instance;
return new Singleton_02();
}
}
- 此种模式虽然是安全的,但由于把锁加到方法上后,所有的访问都因需要锁占用导致资源的浪费。如果不是特殊情况下,不建议此种方式实现单例模式。
3. 饿汉模式(线程安全)
public class Singleton_03 {
private static Singleton_03 instance = new Singleton_03();
private Singleton_03() {
}
public static Singleton_03 getInstance() {
return instance;
}
}
- 此种方式与我们开头的第一个实例化
Map基本一致,在程序启动的时候直接运行加载,后续有外部需要使用的时候获取即可。 - 但此种方式并不是懒加载,也就是说无论你程序中是否用到这样的类都会在程序启动之初进行创建。
- 那么这种方式导致的问题就像你下载个游戏软件,可能你游戏地图还没有打开呢,但是程序已经将这些地图全部实例化。到你手机上最明显体验就一开游戏内存满了,手机卡了,需要换了。
4. 使用类的内部类(线程安全)
public class Singleton_04 {
private static class SingletonHolder {
private static Singleton_04 instance = new Singleton_04();
}
private Singleton_04() {
}
public static Singleton_04 getInstance() {
return SingletonHolder.instance;
}
}
- 使用类的静态内部类实现的单例模式,既保证了线程安全有保证了懒加载,同时不会因为加锁的方式耗费性能。
- 这主要是因为JVM虚拟机可以保证多线程并发访问的正确性,也就是一个类的构造方法在多线程环境下可以被正确的加载。
- 此种方式也是非常推荐使用的一种单例模式
5. 双重锁校验(线程安全)
public class Singleton_05 {
private volatile static Singleton_05 instance;
private Singleton_05() {
}
public static Singleton_05 getInstance(){
if(null != instance) return instance;
synchronized (Singleton_05.class){
if (null == instance){
instance = new Singleton_05();
}
}
return instance;
}
}
- 双重锁的方式是方法级锁的优化,减少了部分获取实例的耗时。
- 同时这种方式也满足了懒加载。
- volatile关键字会强制的保证线程的可见性,而不加这个关键字,JVM也会尽力去保证可见性,但如果CPU一直处于繁忙状态就不确定了。
6. CAS「AtomicReference」(线程安全)
public class Singleton_06 {
private static final AtomicReference<Singleton_06> INSTANCE = new AtomicReference<Singleton_06>();
private static Singleton_06 instance;
private Singleton_06() {
}
public static final Singleton_06 getInstance() {
for (; ; ) {
Singleton_06 instance = INSTANCE.get();
if (null != instance) return instance;
INSTANCE.compareAndSet(null, new Singleton_06());
return INSTANCE.get();
}
}
public static void main(String[] args) {
System.out.println(Singleton_06.getInstance()); // org.itstack.demo.design.Singleton_06@2b193f2d
System.out.println(Singleton_06.getInstance()); // org.itstack.demo.design.Singleton_06@2b193f2d
}
}
- java并发库提供了很多原子类来支持并发访问的数据安全性;
AtomicInteger、AtomicBoolean、AtomicLong、AtomicReference。 - AtomicReference 可以封装引用一个V实例,支持并发访问如上的单例方式就是使用了这样的一个特点。
- 使用CAS的好处就是不需要使用传统的加锁方式保证线程安全,而是依赖于CAS的忙等算法,依赖于底层硬件的实现,来保证线程安全。相对于其他锁的实现没有线程的切换和阻塞也就没有了额外的开销,并且可以支持较大的并发性。
- 当然CAS也有一个缺点就是忙等,如果一直没有获取到将会处于死循环中。
7. Effective Java作者推荐的枚举单例(线程安全)
public enum Singleton_07 {
INSTANCE;
public void test(){
System.out.println("hi~");
}
}
约书亚·布洛克(英语:Joshua J. Bloch,1961年8月28日-),美国著名程序员。他为Java平台设计并实作了许多的功能,曾担任Google的首席Java架构师(Chief Java Architect)。
- Effective Java 作者推荐使用枚举的方式解决单例模式,此种方式可能是平时最少用到的。
- 这种方式解决了最主要的;线程安全、自由串行化、单一实例。
调用方式
@Test
public void test() {
Singleton_07.INSTANCE.test();
这种写法在功能上与共有域方法相近,但是它更简洁,无偿地提供了串行化机制,绝对防止对此实例化,即使是在面对复杂的串行化或者反射攻击的时候。虽然这中方法还没有广泛采用,但是单元素的枚举类型已经成为实现Singleton的最佳方法。
但也要知道此种方式在存在继承场景下是不可用的。
六、总结
- 虽然只是一个很平常的单例模式,但在各种的实现上真的可以看到java的基本功的体现,这里包括了;懒汉、饿汉、线程是否安全、静态类、内部类、加锁、串行化等等。
- 在平时的开发中如果可以确保此类是全局可用不需要做懒加载,那么直接创建并给外部调用即可。但如果是很多的类,有些需要在用户触发一定的条件后(游戏关卡)才显示,那么一定要用懒加载。线程的安全上可以按需选择。
- 建议在学习的过程中一定要加以实践,否则很难完完整整的掌握一整套的知识体系。例如案例中的出现的
Effective Java一书也非常建议大家阅读。另外推荐下这位大神的Github:https://github.com/jbloch
七、推荐阅读
重学 Java 设计模式:实战原型模式-模拟考试试卷乱序题目和答案Java开发架构篇:初识领域驱动设计DDD落地Java开发架构篇:DDD模型领域层决策规则树服务设计Java开发架构篇:领域驱动设计架构基于SpringCloud搭建微服务源码分析(面试常问题目) | Mybatis接口没有实现类为什么可以执行增删改查讲道理,只要你是一个爱折腾的程序员,毕业找工作真的不需要再花钱培训!
重学 Java 设计模式:实战单例模式的更多相关文章
- 重学 Java 设计模式:实战适配器模式
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 擦屁屁纸80%的面积都是保护手的! 工作到3年左右很大一部分程序员都想提升自己的技术 ...
- 重学 Java 设计模式:实战桥接模式(多支付渠道「微信、支付宝」与多支付模式「刷脸、指纹」场景)
作者:小傅哥 博客:https://bugstack.cn - 编写系列原创专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 为什么你的代码那么多ifelse 同类的业务.同样的功能, ...
- 重学 Java 设计模式:实战抽象工厂模式
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获!
- 重学 Java 设计模式:实战装饰器模式(SSO单点登录功能扩展,增加拦截用户访问方法范围场景)
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 对于代码你有编程感觉吗 很多人写代码往往是没有编程感觉的,也就是除了可以把功能按照固 ...
- 重学 Java 设计模式:实战外观模式「基于SpringBoot开发门面模式中间件,统一控制接口白名单场景」
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 你感受到的容易,一定有人为你承担不容易 这句话更像是描述生活的,许许多多的磕磕绊绊总 ...
- 重学 Java 设计模式:实战享元模式「基于Redis秒杀,提供活动与库存信息查询场景」
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 程序员的上下文是什么? 很多时候一大部分编程开发的人员都只是关注于功能的实现,只 ...
- 重学 Java 设计模式:实战代理模式「模拟mybatis-spring中定义DAO接口,使用代理类方式操作数据库原理实现场景」
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 难以跨越的瓶颈期,把你拿捏滴死死的! 编程开发学习过程中遇到的瓶颈期,往往是由于看不 ...
- 重学 Java 设计模式:实战责任链模式「模拟618电商大促期间,项目上线流程多级负责人审批场景」
作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 场地和场景的重要性 射击
- 重学 Java 设计模式:实战迭代器模式「模拟公司组织架构树结构关系,深度迭代遍历人员信息输出场景」
作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 相信相信的力量! 从懵懂的少年,到拿起键盘,可以写一个Hell ...
随机推荐
- 软件——IDEA 超实用使用技巧分享
前言 工欲善其事 必先利其器 最近受部门的邀请,给入职新人统一培训IDEA,发现有很多新人虽然日常开发使用的是IDEA,但是还是很多好用的技巧没有用到,只是用到一些基本的功能,蛮浪费IDEA这个优秀 ...
- PUBG 1V3 线段树扫描线
PUBG 1V3 这个题目我觉得好难写啊. 感觉自己码力不太行啊. 题目大意是,给你n个人,n个人组成m个队伍,每个队伍最多4个人. 然后给你每一个人的位置队伍信息还有攻击范围. 问当一个队伍剩下一个 ...
- Codeforces Round #575 (Div. 3) 昨天的div3 补题
Codeforces Round #575 (Div. 3) 这个div3打的太差了,心态都崩了. B. Odd Sum Segments B 题我就想了很久,这个题目我是找的奇数的个数,因为奇数想分 ...
- Cassandra 简介
Cassandra是云原生和微服务化场景中最好的NoSQL数据库.我信了~ 1. Cassandra是什么 高可用性和可扩展的分布式数据库 Apache Cassandra™是一个开源分布式数据,可提 ...
- python是如何进行参数传递的?
在分析python的参数传递是如何进行的之前,我们需要先来了解一下,python变量和赋值的基本原理,这样有助于我们更好的理解参数传递. python变量以及赋值 数值 从几行代码开始 In [1]: ...
- Excel+Word:Jupyter
直接打开Excel,可以增改删,但如果只是查了?Jupyter Lab/Jupyter Notebook是件利器. 工作内容之一,是复制Excel的一条记录,姓名.身份证号.银行卡号,粘贴在Word的 ...
- Python自动生成100以内加减乘除混合运算题
import random from random import choice ops = ('+','-','×','÷') ans = [] i=0 while i < 100 : op1 ...
- 整理了最全的Python3数据类型转换方法,可以收藏当手册用
本文基于python3.8版本,总结了各种数据类型直接的转换规则和方法.算是比较全了,可以收藏当手册来查. 概述 数据类型转换,指的是通过某种方法,将一个数据由原来的类型转换为另外一个类型.比如,我们 ...
- 【数据结构】平衡树splay和fhq—treap
1.BST二叉搜索树 顾名思义,它是一棵二叉树. 它满足一个性质:每一个节点的权值大于它的左儿子,小于它的右儿子. 当然不只上面那两种树的结构. 那么根据性质,可以得到该节点左子树里的所有值都比它小, ...
- .Net Core3.0 WebApi 项目框架搭建:目录
一.目录 .Net Core3.0 WebApi 项目框架搭建 一:实现简单的Resful Api .Net Core3.0 WebApi 项目框架搭建 二:API 文档神器 Swagger .Net ...