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://xiahunao.cn/news/2799435.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

关于 AC 自动机

什么是 AC 自动机 AC 自动机,全称 Aho-Corasick 自动机,是一种用于字符串搜索的算法,由 Alfred V. Aho 和 Margaret J. Corasick 在 1975 年提出。这个算法是为了解决在一个主文本字符串中查找多个模式字符串(或称为“关键词”&a…

吴恩达机器学习全课程笔记第三篇

目录 前言 P42-P48 神经元和大脑 神经网络中的层 更复杂的神经网络 前向传播(做出预测) P49-P53 代码中的推理 构建一个神经网络 P54-P60 矩阵乘法 TensorFlow框架实现神经网络 前言 这是吴恩达机器学习笔记的第三篇,第二篇笔记…

绿盾限制终端网络访问权限会恢复后,别的网站访问正常就是无法访问钉钉网站和下载东西

环境: Win10 专业版 钉钉7.5.5 绿盾7.0 问题描述: 绿盾限制终端网络访问权限会恢复后,别的网站访问正常就是无法访问钉钉网站和下载东西 解决方案: 排查方法 1.重置浏览器或者更换浏览器测试(未解决&#xff09…

nc开发刚导入项目eclipse出现莫名其妙的错误,红叉,感叹号,文件missing

解决类出现红叉 解决感叹号,文件missing 其他问题 右上角的视图,要选择java,如果是javaEE也会有一些文件没有展示出来。

QYWX企业微信的公告信息限制保存pdf的破解

公司使用企业微信好几年,重大的消息使用公告信息这个模块。可重要的消息无法保存,只能在线收藏。这个玩意只考虑到了维护企业利益,无视员工利益。 后来发现可以利用windows的虚拟打印机,将公告打印成pdf。 用了一段时间&#xf…

IOT-Reaserch安装ghidra以及IDEA和ghidra的配置

Linux research 5.4.0-91-generic #102~18.04.1-Ubuntu SMP Thu Nov 11 14:46:36 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux java --version IOT自带的java是符合要求的,不需要额外下载 iotresearch:~/install-file$ java --version openjdk 11.0.13 2021-10-19 …

【Quasar】quasar轮播图进度条

效果 开始效果 即将结束 结束 码 <template><q-carouselv-model"slide"transition-prev"scale"transition-next"scale"swipeableanimatedinfiniteautoplaynavigationpaddingarrowsheight"300px"class"bg-primary text…

PHP实现分离金额和其他内容便于统计计算

得到的结果可以粘贴到excel计算 <?php if($_GET["x"] "cha"){ $tips isset($_POST[tips]) ? $_POST[tips] : ; $pattern /(\d\.\d|\d)/; $result preg_replace($pattern, "\t\${1}\t", $tips); echo "<h2><strong>数…

ESRI中国培训资料(2013-2018年)

一、2013年培训资料 链接&#xff1a;https://pan.baidu.com/s/1BDQbOlpXGjEE3nLsQowJJg?pwd4j7v 提取码&#xff1a;4j7v 二、2014年培训资料 链接&#xff1a;https://pan.baidu.com/s/1DiDMgrIMz2D-XCAh8jCncA?pwdbfs9 提取码&#xff1a;bfs9 三、2015年培训资料 …

css实现梯形

<div class"trapezoid"></div> .trapezoid {width: 200px;height: 0;border-bottom: 100px solid red; /* 定义梯形的底边 */border-left: 50px solid transparent; /* 定义梯形的左边 */border-right: 50px solid transparent; /* 定义梯形的右边 */} …

Java 2:运算符、表达式和语句

2.1 运算符与表达式 Java提供了丰富的运算符&#xff0c;如算术运算符、关系运算符、逻辑运算符、位运算符等。Java语言中的绝大多数运算符和C语言相同&#xff0c;基本语句如条件分支语句&#xff0c;循环语句等&#xff0c;也和C语言类似。 2.1.1算术运算符与算术表达式 1…

Redis的常见面试题

目录 前言 Redis支持哪些数据类型 五种核心类型 Zset为什么用跳表不用红黑树 &#xff1f; Redis常见的应用场景&#xff1f; 如何检测Redis的连通性&#xff1f; 如何设置key的过期时间&#xff1f; Redis为什么是单线程模型&#xff1f; Redis里的IO多路复用是什…

[计网底层小探索]:实现并部署多线程并发Tcp服务器框架(基于生产者消费者模型的线程池结构)

文章目录 一.网络层与传输层协议sockaddr结构体继承体系(Linux体系)贯穿计算机系统的网络通信架构图示: 二.实现并部署多线程并发Tcp服务器框架线程池模块序列化反序列化工具模块通信信道建立模块服务器主体模块任务回调模块(根据具体应用场景可重构)Tips:DebugC代码过程中遇到…

MySQL学习笔记3: MySQL数据库基础

目录 前言目标数据库操作&#xff08;针对database 的操作&#xff09;1. 创建数据库 create database 数据库名;2. 查看数据库 show databases;3. 选中数据库 use 数据库名;4. 删除数据库 drop database 数据库名; mysql中支持的数据类型1. 数值类型: NUMERIC(M,D)2. 字符串类…

linux platform架构下I2C接口驱动开发

目录 概述 1 认识I2C协议 1.1 初识I2C 1.2 I2C物理层 1.3 I2C协议分析 1.3.1 Start、Stop、ACK 信号 1.3.2 I2C协议的操作流程 1.3.3 操作I2C注意的问题 2 linux platform驱动开发 2.1 更新设备树 2.1.1 添加驱动节点 2.1.2 编译.dts 2.1.3 更新板卡中的.dtb 2.2 …

良好的 API 安全策略的重要性

根据 Cloudflare 2024 年 API 安全与管理报告&#xff0c;到 2024 年&#xff0c;API 请求占全球动态互联网流量的 57%&#xff0c;这证实 API 是现代软件开发的重要组成部分。但随着多年来它们的采用不断增加&#xff0c;相关的安全挑战也随之增加。 在过去两年中&#xff0c…

“目标检测”任务基础认识

“目标检测”任务基础认识 1.目标检测初识 目标检测任务关注的是图片中特定目标物体的位置。 目标检测最终目的&#xff1a;检测在一个窗口中是否有物体。 eg:以猫脸检测举例&#xff0c;当给出一张图片时&#xff0c;我们需要框出猫脸的位置并给出猫脸的大小&#xff0c;如…

Meta 发布 MMCSG (多模态智能眼镜对话数据集)

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

FFmpeg的HEVC解码器源代码学习笔记-1

一直想写一个HEVC的码流解析工具&#xff0c;看了雷神264码流解析工具&#xff0c;本来想尝试模仿写一个相似的265码流分析工具&#xff0c;但是发现265的解码过程和结构体和264的不太一样&#xff0c;很多结构体并没有完全暴露出来&#xff0c;没有想到很好的方法获得量化参数…

HAL STM32 HW I2C DMA + SSD1306/SH1106驱动示例

HAL STM32 HW I2C DMA SSD1306/SH1106驱动示例 &#x1f4cd;硬件I2C DMA驱动参考&#xff1a;https://blog.csdn.net/weixin_45065888/article/details/118225993 &#x1f516;本工程基于STM32F103VCT6&#xff0c;驱动程序独立&#xff0c;可以移植到任意STM32型号上使用。…