【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

  1. 比较V与A是否相等
  2. 如果相等,则将B写入V(交换)
  3. 返回操作是否成功

下面是一段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,如果版本号没有改变,余额就一定没有改变过


🙉本篇文章到此结束

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

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

相关文章

Ajax:回忆与节点

一点回忆 面对我的Ajax学习,实现前后端交互,最开始我采用的使用网络寻找intellij IDEA Ultimate破解方法,然后最终成功,然后按照相关教程配置java ee项目,然后中间又去配置了Tomcat服务器,然后又去学习了一…

1688:开启跨境电商新篇章

引言 在全球化贸易不断深化的今天,跨境电商已成为中小企业拓展国际市场的重要渠道。1688,作为阿里巴巴集团旗下领先的内贸平台,近年来也逐渐发力跨境电商领域,为全球买家提供了一个直通中国工厂的贸易平台。本文将带您深入了解16…

2024-12-06 Unity Addressables3——资源加载

文章目录 1 引用加载1.1 Addressables 的资源引用类1.2 加载资源1.3 加载场景1.4 释放资源 2 Label 介绍3 动态加载3.1 加载单个资源3.2 加载多个资源 Unity 版本:6000.0.26f1c1Addressables 版本:2.3.1 1 引用加载 1.1 Addressables 的资源引用类 Ass…

相机动态/在线标定

图1 图2 基本原理 【原理1】平行线在射影变换后会交于一点。如图所示,A为相机光心,蓝色矩形框为归一化平面,O为平面中心。地面四条黄色直线为平行且等距的车道线。HI交其中两条车道线于H、I, 过G作HI的平行线GM交车道线于M。HI、GM在归一化平面上的投影分别为JK、PN,二者会…

工作:SolidWorks从3D文件导出2D的DWG或DXF类型文件方法

工作:SolidWorks从3D文件导出2D的DWG或DXF类型文件方法 SolidWorks从3D文件导出2D的DWG或2D DXF类型文件方法(一)打开3D文件(二)从装配体到工程图(三)拖出想要的角度的图型(四&#…

【AI系统】低比特量化原理

低比特量化原理 计算机里面数值有很多种表示方式,如浮点表示的 FP32、FP16,整数表示的 INT32、INT16、INT8,量化一般是将 FP32、FP16 降低为 INT8 甚至 INT4 等低比特表示。 模型量化则是一种将浮点值映射到低比特离散值的技术,可…

【ETCD】【源码阅读】ETCD启动流程源码解读

启动流程的图如下: 1、主函数入口 ETCD 启动的入口在 etcd/server/main.go 文件中。 package mainimport ("os""go.etcd.io/etcd/server/v3/etcdmain" )func main() {etcdmain.Main(os.Args) }这里调用了 etcdmain.Main(),这是 …

计算机网络-应用层/运输层

应用层 在上一篇已经提到过, 计算机网络, 最核心的功能就是个产生信息, 发送信息.而并不关注其中的接受方究竟是人, 机器. 而协议, 就是双方约定的 可以表达一定含义的 消息内容. 符合协议的, 就能够被机器解读, 并进行下一步操作, 可能还会返回一定的响应内容. 而应用层, 有…

AD20 原理图库更新到原理图

一 点击工具,从库更新。快捷键TL 二 点击完成 三 执行变更,最后点击关闭

位运算符I^~

&运算:上下相等才是1,有一个不同就是0 |运算:只要有1返回的就是1 ^(亦或)运算:上下不同是1,相同是0 ~运算:非运算,与数据全相反 cpu核心运算原理,四种cpu底层小电路 例&#xf…

Python毕业设计选题:基于django的民族服饰数据分析系统的设计与实现_hadoop+spider

开发语言:Python框架:djangoPython版本:python3.7.7数据库:mysql 5.7数据库工具:Navicat11开发软件:PyCharm 系统展示 管理员登录 管理员功能界面 用户管理 民族服饰管理 看板展示 系统首页 民族服饰 服饰…

mac port 安装redis 并设置为系统服务 自定义配置方法

mac系统中,port 包管理工具比brew的速度快N倍,今天就给大家分享一下在macos系统中如何使用 port安装 redis数据库并配置为服务自动启动和自定义redis.conf配置的方法。 1. 安装redis sudo port install redis 2. 启动redis服务 sudo port load redis …

MySQL导入.sql文件后数据库乱码问题

问题分析: 当导入.sql文件后,发现数据库中的备注出现乱码,通常是由于一下原因导致: 字符集不匹配:.sql文件、MySQL服务器、客户端连接使用的字符集不一致。备注内容编码问题:备注内容本身的编码格式与数据…

RabbitMQ 架构介绍:深入理解与应用

RabbitMQ 是一个开源的消息代理(Message Broker)软件,它实现了高级消息队列协议(AMQP),并提供了可靠的消息传递机制。RabbitMQ 广泛应用于分布式系统中,用于解耦系统组件、异步处理任务和实现消…

【西门子PLC.博途】——在S71200里写时间设置和读取功能块

之前我们在这篇文章中介绍过如何读取PLC的系统时间。我们来看看在西门子1200里面有什么区别。同时也欢迎关注gzh。 我们在S71200的帮助文档中搜索时间后找到这个数据类型 在博途中他是一个结构体,具体为 然后我们再看看它带的读取和写入时间块 读取时间&#xff1…

vue聊天对话语音消息播放动态特效

vue2写法&#xff0c;vue3也能用&#xff0c;粘之即走&#xff1a; 示例&#xff1a; <template><div class"voice-hidden"><divclass"voice-play-chat":class"[className, { animate-stop: !isPlaying }]"><div class&q…

Python 绘图工具详解:使用 Matplotlib、Seaborn 和 Pyecharts 绘制散点图

目录 数据可视化1.使用 matplotlib 库matplotlib 库 2 .使用 seaborn 库seaborn 库 3 .使用 pyecharts库pyecharts库 注意1. 确保安装了所有必要的库2. 检查Jupyter Notebook的版本3. 使用render()方法保存为HTML文件4. 使用IFrame在Notebook中显示HTML文件5. 检查是否有其他输…

【链表】力扣 141. 环形链表

一、题目 二、思路 龟兔进行赛跑 龟的速度是 1&#xff0c;兔的速度是 2龟兔从同一起点出发&#xff0c;若 龟追上兔 则说明 有环 存在&#xff1b;若追不上&#xff0c;则说明无环。 三、代码 /*** Definition for singly-linked list.* class ListNode {* int val;* …

Spring中使用Async进行异步功能开发实战-以大文件上传为例

目录 前言 一、场景再现 1、Event的同步机制 二、性能优化 1、异步支持配置 2、自定义处理线程池扩展 3、将线程池配置类绑定到异步方法 三、总结 前言 在之前的博客中&#xff0c;曾将讲了在SpringBoot中如何使用Event来进行大文件上传的解耦&#xff0c;原文地址&am…

PyTorch 深度学习框架简介:灵活、高效的 AI 开发工具

PyTorch 深度学习框架简介&#xff1a;灵活、高效的 AI 开发工具 PyTorch 作为一个深度学习框架&#xff0c;以其灵活性、可扩展性和高效性广受欢迎。无论是在研究领域进行创新实验&#xff0c;还是在工业界构建生产级的深度学习模型&#xff0c;PyTorch 都能提供所需的工具和…