【Java多线程从青铜到王者】懒汉模式的优化(九)

懒汉模式的问题

在这里插入图片描述
我们看上述的代码,当第一次调用getIntance的时候,intance为null,就会进入if里面,创建出实例,当不是第一次调用的时候,此时的intandce不是null,不进入循环,直接return之前实例好的对象,这样的设定仍然可以保证我们的实例是唯一的,但是创建的时机不一样了,再也不是从程序刚开始驱动的时候开始创建了,而是第一次调用getIntance时开始创建,这个操作的创建时机就不确定了,根据实际的需求,大概率会比饿汉模式要慢一些,也许你整个程序里面就用不到这个方法,于是就省下了创建的开销
有的程序,可能需要根据一定的条件,来判断是否要执行某些操作,是否要实例化实例化一些对象,比如肯德基的疯狂星期四,程序就会判断今天是星期几,根据今天是星期几来判断是否要执行星期四的相关逻辑,如果不是星期四,就不需要执行星期四的相关逻辑了(节省了开销)
我们介绍的单例模式有两种,一个是饿汉模式,一个是懒汉模式,那么问题来了,我们所写的两个代码是否是线程安全的?
在这里插入图片描述
我们先看单例模式(上图),我们的代理模式的实例在程序启动的时候就创建好了,我们调用getIntance方法只是返回我们已经创建好的对象,可以看作是读操作,我们多个线程同时读取同一个对象是不会有线程问题的
在这里插入图片描述
我们再看懒汉模式(上图),if里面的new操作可以看成是写操作,return操作可以看成是读操作,我们在内存可见性那里就讲过了,如果一个线程写的话,同时一个线程读的话,是会有线程安全问题的
在这里插入图片描述
如果有两个线程的执行顺序是上图的样子,就会导致new了两次,我们期望是只有唯一的一个实例,如果是这种情况的话,代码就出现了bug
如何改进懒汉模式呢?
加锁,sychronized
在这里插入图片描述
上图中的代码,我们已经加了锁,多线程的代码非常的复杂,随便改动一点,结果可能都不一样,不一定你加了锁就会线程安全,不加就一定会线程不安全,要根据具体的代码进行分析,一定要确保我们的代码在任意顺序下执行的结果都是正确的,我们此处要是想要代码执行正确的话,是要将if和new两个操作打包成原子的
要不然上图的代码在下图的情况中还是会有线程安全问题
在这里插入图片描述
下图中的加锁方式,就不会出现线程安全问题
在这里插入图片描述
当我们出现上面提到的线程不安全的情况的时候,上图的加锁方式就可以保证线程安全(如下图)
在这里插入图片描述
这样就可以确保,一定是t1执行完new,并且修改了intance的值之后吗,再去执行t2的if,此时t2的if就不会成立了,就不会出现重复new的情况了
我们解决了线程安全的问题,但是我们的代码还是存在一些问题
如果我们已经创建好了intance的话,我们再调用getIntance方法的话,执行的逻辑就都是return intance了,此时就是存粹的读操作了,跟我们的饿汉模式里面的getIntance方法就一样了,此时就没有线程安全问题了,对于一个没有线程安全问题的代码我们进行加锁的话,就会导致效率变低了,为什么效率会变低呢?我们的代码里只要涉及到加锁的问题的话,就会产生因为锁竞争产生的阻塞,阻塞什么时候解除我们不得而知,所以一旦我们的代码里面加了锁,基本就注定于“高性能”无缘了
我们的解决办法是什么呢?
在锁的外面再加上一层if,判断一下intance是否为空,如果为空,说明是第一次调用getInatance,我们执行加锁操作,如果intance为空的话,就说明不是第一次调用,只需要执行return操作(纯粹的读)即可,通过这个if,我们就可根据intance的值的情况判断是否需要进行加锁操作,代码如下图
在这里插入图片描述
但是我们注意到,这个代码我们从来没有见过,这里两个if里面的条件是一样的(如下图)
在这里插入图片描述
在我们之前的单线程/没有阻塞的代码中,两个相同条件的if是没有意义的(只需要写一个即可),但是我们现在的代码涉及到了多线程和阻塞,看起来是两个相同条件的if,其实两个条件的结果是可能不一样的,因为两个if之间有一个加锁的操作,这个加锁可能会导致阻塞,这个阻塞就可能导致intance的值被改变了,两个if之间隔的时间是沧海桑田

第一个if判断是否要加锁
第二个if判断是否要new
两个if的条件虽然一样,但是意义不一样

此时我们已经解决了很多的问题,但是这个代码还是有问题——指令重排序引起的线程安全问题

指令重排序就是在原有逻辑不变的情况下,改变代码的执行顺序,提高程序的效率
在这里插入图片描述

我们上图的new操作就可以拆分成三个大步骤(不是三个指令)
1.申请一段内存空间
2.在这个申请好的内存空间里面调用构造方法,创造出这个实例
3.将这个内存空间的地址赋值给intance
正常情况下,我们的执行顺序是1 2 3,但是编译器也可能会将将这三个步骤的顺序优化成1 3 2,这两种顺序在单线程的情况下都是一样的
但是在多线程的情况下就出现了问题了

在这里插入图片描述
这个代码就会出现上面的问题
想要解上面的问题,还是得使用volatile解决

volatile由两个功能
1.保持内存可见性,每次访问变量必须从内存里面读取,不会优化成从寄存器/缓存里面读取
2.禁止指令重排序,这个被volatile修饰的变量的读写的相关操作的相关指令是不能被重排序的

完整代码如下

class Singleton1{private volatile static Singleton1 intance=null;private static Object locker=new Object();public static Singleton1 getIntance(){if(intance==null){synchronized (locker){if(intance==null){intance=new Singleton1();}}}return intance;}private Singleton1(){}
}

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

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

相关文章

SCI期刊查重参考文献会被查重吗?

查重的时候,参考文献不会被查重。 不管中文还是英文查重系统里一般都有排除参考文献的设置。 比如英文查重系统iThenticate 的排除文献的设置如下: 在iThenticate在线报告界面的右下角点击“漏斗”图标(Filter), ✔…

OpenLayers 获取地图状态

注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图状态信息包括中心点、当前缩放级别、比例尺以及当前鼠标移动位置信息等,在WebGIS开发中,地图状态可以方便快捷的向用户展示基…

JxBrowser 8.8.0 版本发布啦!

一次调用即可下载文件精准清除浏览数据右键点击位置检测获取元素在视口中的位置 🔗 点击此处了解更多详情。 🆓 获取 30 天免费试用。

React 中的TypeScript开发范式

在 TypeScript 中使用 React 可以提高代码的可维护性、可读性和可靠性。TypeScript 提供了静态类型检查和丰富的类型系统,这些功能在 React 开发中非常有用。下面详细介绍如何在 React 项目中使用 TypeScript,并结合泛型和 infer 来定义类型。 1. 项目初…

72道Nginx高频题整理(附答案背诵版)

1. 简述什么是Nginx ? Nginx 是一个开源的高性能HTTP和反向代理服务器,也能够用作IMAP/POP3/SMTP代理服务器。它最初由Igor Sysoev为俄罗斯的一个大型网站Rambler开发,并在2004年首次公开发布。Nginx被设计用来解决C10k问题,即同…

AI时代,数据分析师如何成为不可替代的个体

在数据爆炸的 AI 时代,AI工具正以惊人的速度重塑数据分析行业,数据分析师的工作方式正在经历一场前所未有的变革。数据分析师又该如何破局,让自己不被AI取代呢? 一、AI工具对重复性工作的彻底解构 如以往我们需要花几天写一份数…

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…

Kafka入门-Broker以及文件存储机制

Kafka Broker Broker实际上就是kafka实例,每一个节点都是独立的Kafka服务器。 Zookeeper中存储的Kafka信息 节点的服役以及退役 服役 首先要重新建立一台全新的服务器105,并且在服务器中安装JDK、Zookeeper、以及Kafka。配置好基础的信息之后&#x…

dexcap升级版之DexWild——面向户外环境的灵巧手交互策略:人类和机器人演示协同训练(人类直接带上动捕手套采集数据)

前言 截止到25年6.6日,在没动我司『七月在线』南京、武汉团队的机器的前提下,长沙这边所需的前几个开发设备都已到齐——机械臂、宇树g1 edu、VR、吊架 ​长沙团队必须尽快追上南京步伐 加速前进 如上篇文章所说的, 为尽快 让近期新招的新同…

【基于阿里云搭建数据仓库(离线)】使用UDTF时出现报错“FlatEventUDTF cannot be resolved”

目录 问题: 可能的原因有: 解决方法: 问题: 已经将包含第三方依赖的jar包上传到dataworks,并且成功注册函数,但是还是报错:“FlatEventUDTF cannot be resolved”,如下&#xff1a…

06 Deep learning神经网络编程基础 激活函数 --吴恩达

深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +

【LC实战派】小智固件编译

这篇写给立创吴总,是节前答应他配合git代码的说明;也给所有对小智感兴趣的小伙伴。 请多提意见,让这份文档更有价值 - 第一当然是拉取源码 - git clone https://github.com/78/xiaozhi-esp32.git 完成后,先查看固件中实际的…

有没有 MariaDB 5.5.56 对应 MySQL CONNECTION_CONTROL 插件

有没有 MariaDB 对应 MySQL CONNECTION_CONTROL 插件 背景 写这篇文章的目的是因为昨晚半夜突然被call起来,有一套系统的mysql数据库启动失败了。尝试了重启服务器也不行。让我协助排查一下问题出在哪。 分析过程 一开始拿到服务器IP地址,就去数据库…

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…

【LeetCode】算法详解#6 ---除自身以外数组的乘积

1.题目介绍 给定一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O…

Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践

在 Kubernetes 集群中,如何在保障应用高可用的同时有效地管理资源,一直是运维人员和开发者关注的重点。随着微服务架构的普及,集群内各个服务的负载波动日趋明显,传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…

LLMs 系列科普文(11)

目前我们已经介绍了大语言模型训练的两个主要阶段。第一阶段被称为预训练阶段,主要是基于互联网文档进行训练。当你用互联网文档训练一个语言模型时,得到的就是所谓的 base 模型,它本质上就是一个互联网文档模拟器,我们发现这是个…

深度学习环境配置指南:基于Anaconda与PyCharm的全流程操作

一、环境搭建前的准备 1. 查看基础环境位置 conda env list 操作说明:通过该命令确认Anaconda默认环境(base)所在磁盘路径(如D盘),后续操作需跳转至该磁盘根目录。 二、创建与激活独立虚拟环境 1. 创…

【2D与3D SLAM中的扫描匹配算法全面解析】

引言 扫描匹配(Scan Matching)是同步定位与地图构建(SLAM)系统中的核心组件,它通过对齐连续的传感器观测数据来估计机器人的运动。本文将深入探讨2D和3D SLAM中的各种扫描匹配算法,包括数学原理、实现细节以及实际应用中的性能对比,特别关注…

力扣160.相交链表

题目描述 难度:简单 示例 思路 使用双指针 使用指针分别指向两个不同的链表进行比较 解题方法 1.首先进行非空判断 2.初始化指针分别指向两个链表 3.遍历链表 while (pA ! pB): 当pA和pB不相等时,继续循环。如果pA和pB相等,说明找…