JAVA高并发——单例模式和不变模式

文章目录

  • 1、探讨单例模式
  • 2、不变模式

由于并行程序设计比串行程序设计复杂得多,因此我强烈建议大家了解一些常见的设计方法。就好像练习武术,一招一式都是要经过学习的。如果自己胡乱打,效果不见得好。前人会总结一些武术套路,对于初学者来说,不需要发挥自己的想象力,只要按照武术套路出拳就可以了。等练到了一定的程度,就不必拘泥于套路了。这些武术套路和招数,对应到软件开发中就是设计模式。在这里,我将重点向大家介绍一些并行模式与算法。这些都是前人的经验总结,大家可以在熟知其思想和原理的基础上,再根据自己的需求进行扩展,可能会达到更好的效果。

1、探讨单例模式

单例模式是设计模式中使用最普遍的模式之一。它是一种对象创建模式,用于产生一个对象的具体实例,它可以确保系统中一个类只产生一个实例。在Java中,这样的行为能带来两大好处:

  • (1)对于频繁使用的对象,可以省略new操作花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销。
  • (2)由于new操作的次数减少,对系统内存的使用频率也会降低,这将减轻GC的压力,缩短GC停顿时间。

严格来说,单例模式与并行模式没有直接的关系。讨论这个模式,是因为它实在是太常见了,我们不可避免地会在多线程环境中使用它们。并且,系统中使用单例的地方可能非常多,因此我们非常迫切地需要一种高效的单例实现。

下面给出了一个单例的实现,这个实现是非常简单的,是一个正确并且良好的实现:
在这里插入图片描述
使用以上方式创建单例时有几点必须特别注意。因为我们要保证系统中不会有人意外创建多余的实例,所以我们把Singleton的构造函数设置为私有的。这非常重要,这就警告所有的开发人员,不能随便创建这个类的实例,从而有效避免该类被错误创建。

首先,instance必须是私有的并且静态的。如果不是私有的,那么instance的安全性无法得到保证。一个小小的意外就可能使得instance变成null。其次,因为工厂方法getInstance()必须是静态的,所以对应的instance也必须是静态的。

这个单例的性能是非常好的,由于getInstance()方法只是简单地返回instance,并没有任何锁操作,因此它在并行程序中会有良好的表现。

这种方式有一个明显不足,就是Singleton构造函数或者说Singleton实例在什么时候创建是不受控制的。对于静态成员instance,它会在类第一次初始化的时刻被创建。这个时刻并不一定是getInstance()方法第一次被调用的时刻。

比如,如果你的单例是这样的:
在这里插入图片描述
注意,这个单例还包含一个表示状态的静态成员STATUS。此时,在任何地方引用这个STATUS都会导致instance被创建(任何对Singleton方法或者字段的引用,都会导致类初始化,并创建instance,但是类初始化只有一次,因此instance永远只会被创建一次),比如:
在这里插入图片描述
上述方法会打印出:
在这里插入图片描述
可以看到,即使系统没有要求创建单例,new Singleton()方法也会被调用。

如果大家觉得这个小小的不足并不重要,那么这种单例模式也是一种不错的选择。它容易实现、代码易读而且性能优越。

如果你想精确控制instance的创建时间,那么这种方式就不太友善了。我们需要寻找一种新的方法、一种支持延迟加载的策略,它只会在instance第一次使用时创建对象,具体实现如下:
在这里插入图片描述
这个LazySingleton的核心思想是:最初我们并不需要实例化instance,而是当getInstance()方法被第一次调用时,创建单例对象。为了防止对象被多次创建,我们不得不使用synchronized关键字进行方法同步。这种实现的好处是,充分利用了延迟加载,只在真正需要时创建对象。但坏处也很明显,在并发环境下加锁,竞争激烈的场合对性能可能产生一定的影响。但总体上,这是一个非常易于理解和实现的方法。

上面介绍的两种单例实现可以说是各有千秋。有没有一种方法可以结合二者的优势呢?答案是肯定的。
在这里插入图片描述
上述代码实现了一个单例,并且同时拥有前两种方式的优点。首先getInstance()方法中没有锁,这使其在高并发环境下性能优越。其次,只有在getInstance()方法第一次被调用时,StaticSingleton的实例才会被创建。因为这种方法巧妙地使用了内部类和类的初始化方式。内部类SingletonHolder被声明为私有的,这使得我们不可能在外部访问并初始化它。而我们只可能在getInstance()方法内部对SingletonHolder类进行初始化,利用虚拟机的类初始化机制创建单例。

2、不变模式

在并行软件开发过程中,同步操作似乎是必不可少的。当多线程对同一个对象进行读写操作时,为了保证对象数据的一致性和正确性,有必要对对象进行同步,但是同步操作对系统性能有损耗。为了尽可能地去除这些同步操作、提高并行程序性能,可以使用一种不可改变的对象。依靠对象的不变性,可以确保其在没有同步操作的多线程环境中依然保持内部状态的一致性和正确性。这就是不变模式。

不变模式天生就是多线程友好的,它的核心思想是,一个对象一旦被创建,它的内部状态将永远不会发生改变。没有一个线程可以修改其内部状态和数据,同时其内部状态也绝不会自行发生改变。基于这些特性,对不变对象的多线程操作不需要进行同步控制。

同时需要注意,不变模式和只读属性是有一定的区别的。不变模式比只读属性具有更强的一致性和不变性。对只读属性的对象而言,对象本身不能被其他线程修改,但是对象的自身状态却可能自行修改。

比如,一个对象的存活时间(对象创建时间和当前时间的时间差)是只读的,任何一个第三方线程都不能修改这个属性,但是这是一个可变的属性,因为随着时间的推移,存活时间时刻都在发生变化。而不变模式则要求,无论出于什么原因,对象自创建后,其内部状态和数据都要保持绝对的稳定。

因此,不变模式的主要使用场景需要满足以下两个条件:

  • 当对象创建后,其内部状态和数据不再发生任何变化。
  • 对象需要被共享,被多线程频繁访问。

在Java语言中,不变模式的实现很简单。为确保对象被创建后,不发生任何改变,并保证不变模式正常工作,只需要注意以下四点即可:

  • 去除setter()方法及所有修改自身属性的方法。
  • 将所有属性设置为私有的,并用final标记,确保其不可修改。
  • 确保没有子类可以重载修改它的行为。
  • 有一个可以创建完整对象的构造函数。

以下代码实现了一个不变的产品对象,它拥有序列号、名称和价格三个属性:
在这里插入图片描述
在不变模式的实现中,final关键字起到了重要的作用。对属性的final定义确保所有数据只能在对象被构造时赋值1次。之后,就永远不发生改变。而对class的final定义确保了类不会有子类。根据里氏替换原则,子类可以完全替代父类。如果父类是不变的,那么子类也必须是不变的,但实际上我们无法约束这点,为了防止子类做出一些意外的行为,这里干脆把子类都禁用了。

在JDK中,不变模式的应用非常广泛。其中,最典型的就是java.lang.String类。此外,所有的元数据类、包装类都是使用不变模式实现的。主要的不变模式类型如下:
在这里插入图片描述
由于基本数据类型和String类型在实际的软件开发中应用极其广泛,使用不变模式后,所有实例的方法均不需要进行同步操作,保证了它们在多线程环境下的性能。

注意:不变模式通过回避问题而不是解决问题的态度来处理多线程并发访问控制,不变对象是不需要进行同步操作的。由于并发同步会对性能产生不良的影响,因此,在需求允许的情况下,不变模式可以提高系统的并发性能和并发量。

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

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

相关文章

体验LobeChat搭建私人聊天应用

LobeChat是什么 LobeChat 是开源的高性能聊天机器人框架,支持语音合成、多模态、可扩展的(Function Call)插件系统。支持一键免费部署私人 ChatGPT/LLM 网页应用程序。 地址:https://github.com/lobehub/lobe-chat 为什么要用Lobe…

OpenAI Sora引领AI跳舞视频新浪潮:字节跳动发布创新舞蹈视频生成框架

OpenAI的Sora已经引起广泛关注,预计今年AI跳舞视频将在抖音平台上大放异彩。下面将为您详细介绍一款字节跳动发布的AI视频动画框架。 技术定位:这款框架采用先进的diffusion技术,专注于生成人类舞蹈视频。它不仅能够实现人体动作和表情的迁移…

2.21学习总结

1.【模板】ST 表 2.Balanced Lineup G 3.景区导游 4.最近公共祖先(LCA) 倍增思想:主要用于LCA问题,RMQ问题。在进行 递推 时,如果 状态空间很大,通常的 线性递推 无法满足 时间 与 空间复杂度 的要求&…

Chrome浏览器安装Axure-Chrome-Extension插件

Chrome浏览器打开Axure生成的HTML静态文件页面时,会显示如下图AXURE RP EXTENSION FOR CHROME,这是因为Chrome浏览器没有安装Axure插件Axure-Chrome-Extension导致的。 解决方法: 插件下载地址:https://download.csdn.net/downlo…

通过盲注脚本复习sqllabs第46关order by 注入

在MySQL支持使用ORDER BY语句对查询结果集进行排序处理,使用ORDER BY语句不仅支持对单列数据的排序,还支持对数据表中多列数据的排序。语法格式如下 select * from 表名 order by 列名(或者数字) asc;升序(默认升序) select * from 表名 or…

交换瓶子【第七届】【省赛】【A组】

题目描述 有N个瓶子,编号 1 ~ N,放在架子上。 比如有5个瓶子: 2 1 3 5 4 要求每次拿起2个瓶子,交换它们的位置。 经过若干次后,使得瓶子的序号为: 1 2 3 4 5 对于这么简单的情况,显然&#…

Java练习(第3天)使用StringJoiner类进行字符串连接

一、问题描述 给定2个或多个字符串,将其连接成为一个字符串,并输出连接后字符串的长度。字符串之间用逗号或引号分隔。 样例输入1: Ram Shyam 样例输出1: Ram,Shyam 9 样例输入2(要求接在输出1之后):…

C#_索引器

索引器的作用:令对象可像数组一般被索引 索引器 internal class TestClass {public int[] arr { 1, 2, 3, 4, 5 };public string this[int index] // 前者为返回类型,后者为索引类型// 返回类型代表get函数的返回值类型、set函数的value类型&#xff0…

(done) 什么是正定矩阵?Positive Definite Matrices

正定矩阵的定义:https://baike.baidu.com/item/%E6%AD%A3%E5%AE%9A%E7%9F%A9%E9%98%B5/11030459 正定矩阵的作用、验证视频:https://www.bilibili.com/video/BV1Ag411M76G/?spm_id_from333.337.search-card.all.click&vd_source7a1a0bc74158c6993c…

下一代自动化爬虫神器--playwright,所见即所得,不用逆向不要太香!!!

文章目录 1.Playwright介绍2.与 Selenium 和 pyppeteer 相比,Playwright 具有以下几个区别和优势3.在爬虫中使用 Playwright 的好处4.环境安装5.屏幕录制6.保留记录cookie信息7.playwright代码编写详解1.第一个Playwright脚本(1)同步模式&…

【鼎捷数字化生意经】总说数字化转型?!怎么做才能带来远超你的想象的经济效益呢?他们来告诉你!

编者按: 转型一直在提,2018—2023年,实现数字化转型的企业仅占中国企业的10%,其中实现领军重塑的企业仅占2%。数据看起来并没有那么乐观! 新竞争格局下,企业需要直面挑战,定义新前沿&#xff0…

C++动态分配内存知识点!

个人主页:PingdiGuo_guo 收录专栏:C干货专栏 大家好呀,又是分享干货的时间,今天我们来学习一下动态分配内存。 文章目录 1.动态分配内存的思想 2.动态分配内存的概念 2.1内存分配函数 2.2动态内存的申请和释放 2.3内存碎片问…

⭐北邮复试刷题LCR 018. 验证回文串__双指针 (力扣119经典题变种挑战)

LCR 018. 验证回文串 给定一个字符串 s ,验证 s 是否是 回文串 ,只考虑字母和数字字符,可以忽略字母的大小写。 本题中,将空字符串定义为有效的 回文串 。 示例 1: 输入: s “A man, a plan, a canal: Panama” 输出: true 解释…

如何选择护眼台灯?孩子学习必选五大宝藏台灯推荐!

因为护眼台灯能提供充足明亮的光照,以及很好的保护眼睛预防近视效果,所以现在的儿童青少年书桌上都会有一盏护眼台灯。但是也有很多家长为孩子选择护眼台灯后,反馈护眼台灯的质量参差不齐,很难避免会购买到劣质产品,这…

2023年12月 Python(五级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试(1~6级)全部真题・点这里 一、单选题(共25题,共50分) 第1题 下面代码的输出结果是?( ) dict1 = {1: 10, 2: 20, 3: 30} dict2 <

【安卓基础1】初识Android

&#x1f3c6;作者简介&#xff1a;|康有为| &#xff0c;大四在读&#xff0c;目前在小米安卓实习&#xff0c;毕业入职 &#x1f3c6;本文收录于 安卓学习大全&#xff0c;欢迎关注 &#x1f3c6;安卓学习资料推荐&#xff1a; 视频&#xff1a;b站搜动脑学院 视频链接 &…

Git笔记——2

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一、撤销修改__情况一 二、撤销修改__情况二 三、撤销修改__情况三 四、删除文件 五、理解分支 六、创建、切换和合并分支初体验 七、删除分支 八、合并冲突 总…

idea maven创建kotlin项目

第一步&#xff1a;打开idea 第二步&#xff1a;添加依赖 <repositories><repository><id>mavenCentral</id><url>https://repo1.maven.org/maven2/</url></repository></repositories><build><sourceDirectory>…

ElasticSearch DSL查询、排序 、分页的原理及语法

1. DSL查询分类和基本语法 ElasticSearch提供了基于Json的DSL来定义查询&#xff0c;常见的查询类型包括&#xff1a; • 查询所有&#xff1a;查询出所有数据&#xff0c;一般测试用&#xff0c;一般不是查出所有&#xff0c;一次性查询20条。例如 match_all • 全文检索(ful…

【webrtc】m77 PacedSender

mediasoup是m77的代码,m77的代码并没有paced controller ,而且与paced sender 的逻辑混在了一起。结合大神们的代码分析,对照m77 进行 理解。m77 有ProbeController。给pacersender 更新飞行数据:PacedSender::InsertPacket(size_t bytes) 对应的是 PacingController::OnPa…