【JavaEE】多线程(6)
一、用户态与内核态
【概念】
用户态是指用户程序运行时的状态,在这种状态下,CPU只能执行用户态下的指令,并且只能访问受限的内存空间
内核态是操作系统内核运行时的状态,内核是计算机系统的核心部分,CPU可以执行所有指令,可以访问所有内存空间
【 两者切换原因】
当用户程序需要执行一些需要操作系统支持的操作时,需要将用户态切换到内核态
【举例】
线程的阻塞与唤醒就需要用户态与内核态切换
当线程被阻塞时,线程会从用户态切换到内核态,操作系统内核会处理阻塞请求将线程的状态设置为阻塞,并将其添加到等待队列中
等线程被唤醒时,操作系统会在内核态中将线程的状态改为就绪并将其从等待队列中移除,切换到用户态后,继续执行用户态下的指令
【注意】
用户态与内核态之间切换的开销非常大,因此,减少不必要的用户态与内核态之间的切换对于系统性能和效率提高很重要
二、锁策略
2.1 什么是锁策略
锁策略是指在多线程编程中,这把锁在加锁、解锁、锁冲突时都会怎么做
2.2 乐观锁 vs 悲观锁
悲观锁认为多个线程访问同⼀个共享变量冲突的概率较大,会在每次访问共享变量之前都去真正加锁
乐观锁认为多个线程访问同⼀个共享变量冲突的概率不大,并不会真的加锁,而是直接尝试访问数据
在访问的同时识别当前的数据是否出现访问冲突.
【Java中synchronized是哪种锁】
synchronized既是乐观锁也是悲观锁,因为它支持自适应
synchronized在开始的时候会使用乐观锁,当发现锁竞争的次数增加时会切换为悲观锁
2.3 重量级锁 vs 轻量级锁
一般认为悲观锁就是重量级锁,乐观锁就是轻量级锁
重量级锁加锁的过程做的事情多——重量;轻量级锁加锁的过程做的事情少——轻量
synchronized是一个轻量级锁,如果锁冲突比较严重就会变成重量级锁
2.4 自旋锁 vs 挂起等待锁
自旋锁是轻量级锁的一种典型实现方式,下面是自旋锁一段伪码:
while (true) {if (锁是否被占用) {continue;}获取到锁break;
}
CPU在忙等、空转,如果获取锁失败,就立即再尝试获取锁,无限循环,直到获取到锁为止;消耗了更多的CPU资源,但是锁一旦被释放,就会第一时间拿到锁
自旋锁轻量的原因:一方面自旋锁避免了线程的阻塞与唤醒的开销,减少了性能的消耗;另一方面自旋锁一般适用于线程占用锁时间较少的场景,不会造成过多CPU资源
拿到锁的速度更快,但消耗CPU
挂起等待锁是重量级锁的一种典型实现方式,借助系统中的线程调度,如果当前锁被占用,该线程尝试获取锁,就会挂起(阻塞状态),直到这个锁被释放,系统调度到这个线程,该线程才会尝试获取这个锁
挂起等待锁重量的原因:需要进行线程的阻塞与唤醒,有较多的用户态与内核态之间的切换,重量
拿到锁的速度更慢,节省CPU
synchronized 轻量级锁部分是基于自旋锁实现的,重量级锁部分是基于挂起等待锁实现的
2.5 可重入锁 vs 不可重入锁
可重入锁:同一个线程,针对同一把锁,连续加锁两次,不会死锁
不可重入锁:同一个线程,针对同一把锁,连续加锁两次,会死锁
synchronized是可重入锁
2.6 公平锁 vs 非公平锁
公平锁:严格按照先来后到的顺序来获取锁,哪个线程等待的时间长,哪个线程就先拿到锁
非公平锁:多个线程随机获取到锁,和线程等待时间无关
synchronized属于非公平锁
2.7 互斥锁 vs 读写锁
synchronized是互斥锁
读写锁是一个比较特殊的锁,先来看下面几个有关线程安全场景:
- 两个线程只读一个共享数据,不会发生线程安全问题
- 两个线程写一个共享数据,会发生线程安全问题
- 两个线程一个都一个写,会发生线程安全问题
读写锁拥有一下功能:
- 读锁和读锁之间不会发生互斥——有利于降低锁冲突的概率
- 写锁和写锁之间会发生互斥
- 读锁和写锁之间会发生互斥
synchronized不是读写锁,因为加上synchronized后,即使是两个都只读共享变量也会产生互斥
三、synchronized 实现原理
3.1 特点
- 既是悲观锁,也是乐观锁
- 既是轻量级锁,也是重量级锁;轻量级锁基于自旋锁实现,重量级锁基于挂起等待所实现
- 可重入锁
- 非公平锁
- 是互斥锁,不是读写锁
3.2 synchronized 自适应
什么是偏向锁:
代码首次执行synchronized对对象加锁时并不是真正加锁,而是作一个标记,如果后续没有其他线程针对这个对象加锁的话,就一直保持这种状态,直到解锁,这样就减少了系统开销
当后续有其他线程占用同一个锁对象加锁时,才会真正加锁,此时就已升级成了轻量级锁
3.3 锁消除
锁消除是一种锁优化策略
当在代码中写了加锁的操作,编译器&JVM会对你当前的代码进行检查,看这个锁加的是否合适,如果完全没必要加锁,就会把加锁操作优化掉
比如在单线程的环境下进行加锁操作,该操作就会被编译器优化掉
3.4 锁粗化
锁的粒度:当加锁的范围内,进行的操作越多,锁的粒度越粗,反之,锁的粒度越细
在保证逻辑等价的情况下,为了避免频繁加锁解锁,编译器会将多次细粒度的锁,合并成一次粗粒度的锁
四、CAS
4.1 什么是CAS
CAS(compare and swap),意为比较和交换,一个CAS设计以下操作
假设内存中的值为V,旧的预期值为A,要修改的值为B
- 比较V与A是否相等
- 如果相等,则将B写入V(交换)
- 返回操作是否成功
下面是一段CAS的伪码:
boolean CAS (address, exceptValue, swapValue) {if (&address == exceptValue) {address = swapValue;return true;}return false;
}
注意:上述代码并不是原子的,真实的CAS是一个原子硬件指令,改代码只是辅助理解
当多个线程针对某一资源进行CAS操作,只有一个线程操作成功,但是其他线程并不会阻塞,而是收到操作失败的信号
4.2 CAS是怎么实现的
简而言之,是因为硬件方面提供了支持,软件层面才可以做到,由于CPU提供了CAS对应的硬件指令,因此操作系统内核也能够完成这样的操作,之后OS会提供出CAS的api,JVM对OS提供的api进一步的封装,我们便可以在Java中使用CAS操作了
4.3 CAS 的应用
1)原子类
标准库中提供了 java.util.concurrent.atomic 包,里面的类都是基于CAS实现的原子类:
我们以 AtomicInteger 类为例:
public class Demo {public static void main1(String[] args) {AtomicInteger count = new AtomicInteger(1);count.getAndIncrement(); // count++count.incrementAndGet(); // ++countcount.getAndDecrement(); // count--count.decrementAndGet(); // --countcount.getAndAdd(100); //count += 100}
}
上述代码的加加减减操作都是原子的,没有用到任何加锁操作
接下来以其中一个方法为例进行详细剖析:看getAndIncrement()的伪代码
class AtomicInteger {private int value;public int getAndIncrement() {int oldValue = value;while ( CAS(value, oldValue, oldValue+1) != true) {oldValue = value;}return oldValue;}
}
假设两个线程同时调用getAndIncrement
1. 两个线程都读取value的值到oldValue中(oldValue是一个局部变量,每个线程都有自己的栈)
2. 线程1先执行CAS,发现oldValue 和 value 相同,则直接对value 赋值 oldValue + 1,注意这里是getAndIncrement,所以先获取再加加,所以返回的是oldValue,但其实value已经加1了
3. 线程2再执行CAS的时候,发现value 和 oldValue不相等,则进入循环,在循环里重新获取value的值并赋值给oldValue
4. 线程2第二次执行CAS,发现oldValue 和 value相同,于是执行赋值操作
5. 线程1和线程2针对同一个变量进行加加操作,整个过程线程是安全的并且没有用到锁
2)实现自旋锁
上述线程2在循环中重新将value赋值给oldValue的操作很像自旋锁的实现逻辑,实际上,自旋锁就是基于CAS实现的,来看伪代码:
public class SpinLock {private Thread owner = null; //此时owner处于未加锁状态public void lock(){while(!CAS(this.owner, null, Thread.currentThread())){}}public void unlock (){this.owner = null;}
}
代码中owner用来追踪加锁的线程,如果为null,代表代码中没有任何一个线程加锁,接下来有一个线程1调用lock()方法进行加锁,执行CAS,发现owner为空,则直接进行加锁,并owner指向这个加锁的线程,CAS执行成功,返回true,取反后跳出while循环
此时又来一个线程2也要进行加锁(假设这里锁对象和线程1相同),调用lock()方法,发现owner不为空,说明有其他线程进行了加锁,那就进入循环,并不断尝试CAS操作
当线程1解锁后,调用unlock()方法,此时owner为空,线程2执行CAS操作成功,成功加锁并跳出循环
4.4 CAS 的 ABA 问题
CAS的核心是:比较发现相等→交换,CAS希望的是数据从来没改变过(相等)但是某些情况,可能会有其他线程将数据从A→B→A,CAS并不能判断数据中途是否有发生改变,这就是ABA问题
ABA在一些极端情况下可能产生bug,开下面一段取款的伪代码:
void 取款 () {int oldBalance = balance; // balance 为当前账户余额// CAS执行成功,取款500while (!CAS(balance, oldBalance, balance - 500)) {}}
假如我的初衷就是取500块钱,取款机创建了两个线程来并发执行-500操作,我们希望一个-500成功,一个-500失败
此时如果加一个转账的操作就会引发bug
如何避免ABA问题:
上述场景中,用余额来判定本身就不太科学,因为余额会发生改变,容易引发ABA问题
引入版本号,约定版本号只能加 不能减,每次操作余额版本号都要+1,如果版本号没有改变,余额就一定没有改变过
🙉本篇文章到此结束
相关文章:

【JavaEE】多线程(6)
一、用户态与内核态 【概念】 用户态是指用户程序运行时的状态,在这种状态下,CPU只能执行用户态下的指令,并且只能访问受限的内存空间 内核态是操作系统内核运行时的状态,内核是计算机系统的核心部分,CPU可以执行所有…...

BERT和RoBERTa;双向表示与单向的简单理解
目录 BERT和RoBERTa大型预训练语言模型 BERT的原理 RoBERTa的原理 举例说明 双向表示与单向的简单理解 除了预训练语言模型,还有什么模型 一、模型类型与结构 二、训练方式与数据 三、应用场景与功能 四、技术特点与优势 BERT和RoBERTa大型预训练语言模型 BERT(Bi…...
Pytorch使用手册-计算机视觉迁移学习教程(专题十三)
在本教程中,你将学习如何使用迁移学习训练一个卷积神经网络进行图像分类。更多关于迁移学习的内容可以参考 CS231n 课程笔记。 引用课程笔记中的内容: 实际上,很少有人从头开始训练一个完整的卷积网络(随机初始化),因为拥有足够大数据集的情况相对罕见。相反,通常会在非…...
Jackson - Java对象与JSON相互转换
在这篇文章中,我将向您展示如何使用Jackson-databind API来实现Java对象与JSON之间的绑定,以及如何将JSON数据转换为Java对象。 对于Java开发者来说,将JSON转换为Java对象及反向操作是一个常见的任务,因此我将通过示例演示如何完…...
怎麼解決路由器IP地址衝突?
路由器IP地址衝突通常發生在網路中有兩個設備嘗試使用相同的IP地址時。這種衝突會導致網路連接問題,因為每個設備需要一個唯一的IP地址才能正常通信。 1. 重啟設備 重啟路由器和設備:有時候簡單的重啟可以解決問題,設備重新獲取一個新的IP地…...

趣味数学 2.3.7 | 完全免费,无注册登录,简约纯净
趣味数学是一款完全免费的数学学习软件,支持安卓系统。它无需登录注册,界面简约纯净,分类详细,涵盖趣味数学、数学初练、应用计算、数字推理、图形推理、数字2048、题目练习和数学知识等多个分类。每个分类包含丰富的题目和关卡&a…...

Oracle ASM特性介绍和增删盘操作
1. 介绍 1.1. 在没有ASM之前ORACLE数据库靠什么去解决存储问题: 裸设备:裸设备就是没有被文件系统格式化的分区或者是直接挂载到操作系统上的磁盘。ORACLE可以直接将数据写入到裸设备中,读写能非常优异。像ORACLE的数据文件、控制文件、REDO日志在过去…...
深度优先搜索迷宫路径
深度优先搜索迷宫路径 问题描述 我们需要编写一个程序,通过深度优先搜索(DFS)找到从迷宫左上角到右下角的一条通路。 迷宫的表示: 迷宫由 0 和 1 构成的二维数组表示。0 表示可以走的路径,1 表示障碍。用户输入迷宫的…...
多媒体技术的 发展阶段----高中信息技术教资面试
上课,同学们好!请坐 在正式上课之前,老师带来 了一段微课视频,请同学们认真观看大屏幕。等下来回答老师的问题。 好,视频播放完成了,现在老师想问问大家。大家从视频中都看到了什么尼?好&…...

行为型设计模式之《责任链模式》实践
定义 责任链模式(Chain Of Responsibility Pattern)顾名思义,就是为请求创建一条处理链路,链路上的每个处理器都判断是否可以处理请求,如果不能处理则往后走,依次从链头走到链尾,直到有处理器可…...

中酱黑松露手工古法酱油,邂逅独特 “酱油红”
在美食的世界里,调味品往往扮演着画龙点睛的角色,它们虽不似主食材那般夺目,却能悄无声息地赋予菜肴灵魂与韵味。而今天,要带大家走进的,便是中酱手工古法酱油所营造出的独特美味天地,去领略那一抹别具魅力…...
Java NIO channel
channel(通道),byteBuffer(缓冲区),selector(io多路复用),通道FileChannel,SocketChannel的transferTo,transferFrom,MappedByteBuffer实现了零拷贝。 JVM调操作系统方法,read,write,都可以送字…...

智能交通(8)——腾讯开悟智能交通信号灯调度赛道
本文档用于记录参加腾讯开悟智能信号灯调度赛道的模型优化过程。官方提供了dqn和target_dqn算法,模型的优化在官方提供的代码基础上进行。最终排名是在榜单16,没能进入最后的决赛。 一.赛题介绍 赛题简介:在本地赛题中,参赛团队…...

ip所属地址是什么意思?怎么改ip地址归属地
在数字化时代,IP地址作为网络设备的唯一标识符,不仅关乎设备间的通信,还涉及到用户的网络身份与位置信息。IP所属地址,即IP地址的归属地,通常反映了设备连接互联网时的地理位置。本文将深入解析IP所属地址的含义&#…...

攻防世界 ctf刷题 新手区1-10
unserialize3 因为我上个笔记写了 php返序列化 所以先趁热打铁 看这个题目名字 我们就知道是 反序列化呀 因为flag有值所以 我们先输个 111 看看有没有线索 没线索但是这边 有个发现就是他是使用get方式传参的 可能他会把我们的输入 进行传入后台有可能进行反…...
Node做一个自动删除指定文件和文件夹工具
node14 可以搭配脚手架工具实现自动实现删除 // 引入path模块,用于处理文件路径 const path require(path); // 引入fs模块的promises API,用于异步文件操作 const fs2 require(fs).promises; // 引入fs模块,用于同步文件操作 const fs …...

陈若尧新歌《一来二去》陆续登陆全球音乐平台
由青年演员,歌手陈若尧带来的全新创作单曲《一来二去》由索尼音乐发行,于2024年11月18日陆续全球上线。这也是陈若尧与索尼音乐合作的第一首单曲。探索古典风格与流行音乐的新结合。歌曲上线不久,就因优美抒情的动人旋律,诗意而意味深远的歌词…...

【Docker】针对开发环境、测试环境、生产环境如何编排?
目录 一、引言 二、Docker Compose 文件基础 三、针对不同环境的 Docker 编排 开发环境 测试环境 生产环境 四、配置文件全局变量的编写 五、总结 一、引言 在软件开发和部署的过程中,不同的环境有着不同的需求和配置。Docker 作为一种强大的容器化技术&…...

小程序项目的基本组成结构
分类介绍 项目根目录下的文件及文件夹 pages文件夹 用来存放所有小程序的页面,其中每个页面都由4个基本文件组成,它们分别是: .js文件:页面的脚本文件,用于存放页面的数据、事件处理函数等 .json文件:…...
001-mysql安装
[rootcentos701 ~]# hostname -I 10.0.0.200 172.17.0.1 [rootcentos701 ~]# hostname centos701 [rootcentos701 ~]# rpm -qa | grep mariadb [rootcentos701 ~]# rpm -e --nodeps mariadb-libs-5.5.65-1.el7.x86_64 [rootcentos701 ~]# useradd mysql -s /sbin/nologin #创建…...

wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...

C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...

逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...