【JavaEE】锁策略
文章目录
- 前言
- 1. 乐观锁和悲观锁
- 2. 重量级锁和轻量级锁
- 3. 自旋锁和挂起等待锁
- 4. 公平锁和非公平锁
- 5. 可重入锁和非可重入锁
- 6. 读写锁
- Java synchronized 分别对应哪些锁策略
- 1. 乐观锁和悲观锁
- 2. 重量级锁和轻量级锁
- 3. 自旋锁和挂起等待锁
- 4. 公平锁和非公平锁
- 5. 可重入锁和非可重入锁
- 相关面试题
前言
在前面的多线程中,我们学习了为了解决线程不安全问题,使用 synchronized 为线程进行加锁,但是作为程序员光知道如何使用锁还不行,还需要知道有哪些锁策略。今天我将为大家分享在多线程中有哪些锁策略。
1. 乐观锁和悲观锁
悲观锁是一种基于悲观态度的锁机制,它假定最坏的情况,即在修改数据之前,它会先将数据锁住,阻止任何人对数据进行操作,直到它释放锁。这种锁的机制可以完全保证数据的独占性和正确性,因为每次请求都会先对数据进行加锁,然后进行数据操作,最后再解锁。然而,由于加锁和解锁的过程会造成消耗,所以这种策略的性能不高。
乐观锁则是一种基于乐观态度的锁机制,它假定不会发生数据冲突,只在提交操作的时候才锁定数据。这意味着,在数据提交之前,其他进程可以继续对数据进行操作。乐观锁可以实现并行操作,因此具有较高的性能。但需要注意的是,如果发生数据冲突,需要由数据库系统进行处理。
乐观锁和悲观锁都是计算机对后面发生事情的预测。悲观锁会觉得后面发生锁冲突的现象比较严重,所以在修改数据之前就会上锁;而乐观锁则觉得后面发生锁冲突的现象不严重,所以在处理数据之前就不会进行加锁。
给大家举个简单的例子:加入我们刚来到一个城市,周末的时候我想去体育馆打篮球,但是因为是刚来,不知道体育馆几点开门。那么这时候,悲观锁的做法就是:我现在家等着吧,问问朋友体育馆啥时候开门,但是朋友可能还在睡觉,所以我就只能在家等着;而乐观锁则是:”现在都8点了,体育馆应该开门了,我先过去,如果开门了就可以直接进去了,就算没开门,我也可以在体育馆外面等一会“。
2. 重量级锁和轻量级锁
重量级锁和轻量级锁是站在工作量的角度来划分的。
重量级锁基于操作系统的互斥量(Mutex Lock)而实现的锁,会导致进程在用户态和内核态之间切换,相对开销较大。例如,synchronized在内部基于监视器锁(monitor)实现,监视器锁基于底层的操作系统的Mutex Lock实现,因此在这种情况下synchronized属于重量级锁,重量级锁需要在用户态和核心态之间做转换。
- 大量的内核态用户态切换
- 很容易引发线程的调度
轻量级锁则是相对与重量级锁而言的,轻量级锁的核心设计实在没有多线程竞争的前提下,减少重量级锁的使用以提高系统性能。 轻量级锁适用于线程交替执行同步代码块的情况(既互斥操作),如果同一时刻与多个线程访问同一个锁,则将会导致轻量级锁膨胀为重量级锁。轻量级锁在发生线程竞争时,会让出 CPU 的执行权限,以便其他线程可以继续工作。
- 少量的内核态用户态切换.
- 不太容易引发线程调度
为什么重量级锁的用户态和内核态之间的转换效率会更低呢?
用户态的读写速率比内核态更慢。在内核态,CPU可以访问内存的所有数据,包括外围设备如硬盘、网卡等。同时,CPU也可以将自己从一个程序切换到另一个程序。而在用户态,程序只能受限地访问内存,且不允许访问外围设备。这种情况下,如果用户态的程序需要访问外围设备,如硬盘,那么它必须先切换到内核态,再由内核态进行系统调用来读写磁盘,这个过程会导致额外的开销。
为什么内核态操作与用户态的操作相比效率较低呢?
内核态的实现会占用内核稀缺的资源,例如操作系统需要维护线程列表,一旦操作系统装载后就无法动态改变。并且线程的数量远远大于进程的数量,随着线程数的增加,内核将耗尽,从而导致效率降低。此外,每次线程切换到内核态都需要陷入到内核,由操作系统来调度,这个过程也会花费一定的时间。
3. 自旋锁和挂起等待锁
自旋锁是一种轻量级锁,当一个线程尝试获取锁失败时,它会一直循环尝试获取锁,直到成功为止。这种机制消耗大量的CPU资源,因为它会使线程不断地尝试获取锁,无法做其他的工作。自旋锁只在加锁失败时进行忙等待,不会阻塞线程。
//这是一个自旋锁的伪代码
//当getLocker的返回结果为false的时候,表示未获取到锁,那么就继续循环
while(getLocker == false) {
}
挂起等待锁是重量级锁的一种典型实现。当一个线程尝试获取锁失败时,它会通过内核的机制挂起等待,直到锁被释放。此时,线程会释放CPU资源,让其他线程可以执行。当锁被释放时,挂起的线程会重新尝试获取锁。挂起等待锁在等待期间会阻塞线程,导致线程无法做其他的工作。
//挂起等待锁,未获取到锁则是进入阻塞等待,等待内核态操作获取到锁
synchronized (locker) {
}
自旋锁虽然会消耗 CPU 资源,但换来的是更快的响应速度。
这两种锁的选用取决于具体的应用场景和需求。如果线程间的交互非常频繁,且锁被持有的时间比较短,那么自旋锁可能更合适。如果线程间的交互比较少,且锁被持有的时间比较长,那么挂起等待锁可能更合适。
当线程想要获取锁但是这个锁又被别的线程获取到的时候,挂起等待锁会进入阻塞等待状态,因为进入阻塞的线程什么时候被唤醒是个不确定因素,它是由内核态操作决定的,所以就会导致程序的执行速度下降;而自旋锁则不会进入阻塞等待状态,而是不断循环判断这个锁时候还被占有,如果这个锁还被占有,那么自旋锁还会继续循环,直到这个锁被释放,当这个锁被释放的时候该线程就可以获取到该锁,保证了整个操作都处于用户态的操作。
4. 公平锁和非公平锁
公平锁和非公平锁是两种常用的线程同步机制,用于在多线程环境下保护共享资源。
公平锁是指多个线程按照请求锁的顺序获取锁,即先到先得的原则。在公平锁中,如果有多个线程等待获取锁,那么锁会依次分配给等待时间最长的线程,这样可以避免线程饥饿的情况。公平锁的实现比较复杂,需要维护一个线程等待队列,因此性能会比较低。
非公平锁是指多个线程按照竞争获取锁的顺序获取锁,即先到不一定先得的原则。在非公平锁中,如果有多个线程等待获取锁,那么锁可能会直接分配给等待时间较短的线程,这样可能会导致一些线程一直无法获取锁,出现线程饥饿的情况。非公平锁的实现比较简单,不需要维护一个线程等待队列,因此性能会比较高。
5. 可重入锁和非可重入锁
可重入锁是指同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提是锁对象得是同一个对象),不会因为之前已经获取过锁还没有释放而阻塞。可重入锁的一个优点是可以避免死锁。
非可重入锁则是相反,线程获取锁后,内部不能再获取锁,由于之前已经获取过还没释放而阻塞,可能会导致线程死锁。
6. 读写锁
我们通常对数据的操作就是读操作和写操作,如果是单线程的话,读和写操作不会发生问题,但是如果发生在多线程当中的话就会出现一些问题,当我一个线程在读数据的时候,另一个线程在写这个数据,那么到最后读取的数据就与数据本身不一样;当两个线程都进行写操作的时候,最终的结果也不是正确的结果,而多个线程同时进行读操作的时候是不会发生问题的。也就是说只有多个线程同时进行读操作的时候才不会发生线程安全的问题,那么该如何解决读操作和写操作在多线程中出现的问题呢?
读写锁,当多个线程同时进行读操作的时候不会发生互斥的问题,如果多个线程同时进行读操作和写操作或者同时进行写操作的时候,读写锁就是互斥的,后面的线程就无法获取到这个锁。
读写锁的特点是:
- 读读不互斥:多个线程可以同时读取共享资源,因为读操作本身是线程安全的。
- 读写互斥:当有一个线程正在写共享资源时,其他线程不能进行读或写操作,因为读写操作是互斥的,以防止数据不一致或数据竞争。
- 写写互斥:当有两个或多个线程同时写共享资源时,会产生互斥现象,以防止数据互相干扰。
Java synchronized 分别对应哪些锁策略
1. 乐观锁和悲观锁
对于乐观锁和悲观锁来说, synchronized 属于自适应锁。synchronized 一开始属于乐观锁,但是如果计算机预测到后面发生锁冲突的现象较严重的话,synchronized 就会转变为悲观锁。
2. 重量级锁和轻量级锁
对于重量级锁和轻量级锁来说,synchronized 也是属于自适应锁。开始由于线程的工作量较小,synchronized 是轻量级锁,但是如果到后面线程需要处理的工作量较大的话,synchronized 又会转变为重量级锁。
3. 自旋锁和挂起等待锁
对于自旋锁和挂起等待锁来说,synchronized 属于自适应锁。当锁冲突的现象不严重的时候,synchronized 为自旋锁,但是如果锁冲突现象较严重的话,synchronized 又会转换为挂起等待锁。
4. 公平锁和非公平锁
synchronized 属于非公平锁,当多个线程尝试获取同一把锁的时候,synchronized 不管你先来后到的顺序,而是所有等待的线程竞争这把锁。
5. 可重入锁和非可重入锁
synchronized 属于可重入锁,当一个线程在外面获取到这个锁的时候,在内部也会自动获取到这个锁,而不会陷入死锁的状态。
synchronized 不属于读写锁。
相关面试题
1) 你是怎么理解乐观锁和悲观锁的,具体怎么实现呢?
悲观锁认为多个线程访问同一个共享变量冲突的概率较大, 会在每次访问共享变量之前都去真正加锁。
乐观锁认为多个线程访问同一个共享变量冲突的概率不大. 并不会真的加锁, 而是直接尝试访问数据. 在访问的同时识别当前的数据是否出现访问冲突.
悲观锁的实现就是先加锁(比如借助操作系统提供的 mutex), 获取到锁再操作数据. 获取不到锁就等待.
乐观锁的实现可以引入一个版本号. 借助版本号识别出当前的数据访问是否冲突.
2) 介绍下读写锁?
- 读写锁就是把读操作和写操作分别进行加锁.
- 读锁和读锁之间不互斥.
- 写锁和写锁之间互斥.
- 写锁和读锁之间互斥.
- 读写锁最主要用在 “频繁读, 不频繁写” 的场景中.
3) 什么是自旋锁,为什么要使用自旋锁策略呢,缺点是什么?
如果获取锁失败, 立即再尝试获取锁, 无限循环, 直到获取到锁为止. 第一次获取锁失败, 第二次的尝试会在极短的时间内到来. 一旦锁被其他线程释放, 就能第一时间获取到锁.
相比于挂起等待锁,
优点: 没有放弃 CPU 资源, 一旦锁被释放就能第一时间获取到锁, 更高效. 在锁持有时间比较短的场景下非常有用.
缺点: 如果锁的持有时间较长, 就会浪费 CPU 资源
4) synchronized 是可重入锁么?
是可重入锁.
可重入锁指的就是连续两次加锁不会导致死锁.
实现的方式是在锁中记录该锁持有的线程身份, 以及一个计数器(记录加锁次数). 如果发现当前加锁的线程就是持有锁的线程, 则直接计数自增.
相关文章:
【JavaEE】锁策略
文章目录 前言1. 乐观锁和悲观锁2. 重量级锁和轻量级锁3. 自旋锁和挂起等待锁4. 公平锁和非公平锁5. 可重入锁和非可重入锁6. 读写锁Java synchronized 分别对应哪些锁策略1. 乐观锁和悲观锁2. 重量级锁和轻量级锁3. 自旋锁和挂起等待锁4. 公平锁和非公平锁5. 可重入锁和非可重…...
在 SDXL 上用 T2I-Adapter 实现高效可控的文生图
T2I-Adapter 是一种高效的即插即用模型,其能对冻结的预训练大型文生图模型提供额外引导。T2I-Adapter 将 T2I 模型中的内部知识与外部控制信号结合起来。我们可以根据不同的情况训练各种适配器,实现丰富的控制和编辑效果。 同期的 ControlNet 也有类似的…...
Python分支结构和循环结构
嗨喽~大家好呀,这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 一.分支结构 分支结构是根据判断条件结果而选择不同向前路径的运行方式,分支结构分为:单分支,二分支和多分支。 1࿰…...
Unity调用API函数对系统桌面和窗口截图
Unity3D调用WINAPI函数对系统窗口截图 引入WINAPI函数调用WINAPI函数进行截图使用例子 引入WINAPI函数 using System; using System.Collections; using System.Runtime.InteropServices; using System.Drawing;[DllImport("user32.dll")]private static extern Int…...
【问题思考总结】CPU怎么访问磁盘?CPU只有32位,最多只能访问4GB的空间吗?
问题 在学习操作系统的时候发现了这样一个问题,32位的CPU寻址空间只有4GB,难道只有4GB的空间可以使用吗?以此为始,我开始了一些思考。 思考 Q1:首先,我似乎混淆了一个概念,内存和外存&#x…...
UG NX二次开发(C++)-CAM-根据刀具对程序组进行重新分组
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1、前言2、在UG NX中创建一个三维模型3、在UG NX/CAM中创建多个加工程序4、采用UG NX二次开发(NXOpen)实现按照刀具分组程序组4.2 创建UI Styler4.1 实现逻辑4.2 生成的代码如下:4.3 测试效果4.…...
Unity如何实现TreeView
前言 最近有一个需求,需要实现一个TreeView的试图显示,开始我一直觉得这么通用的结构,肯定有现成的UI组件或者插件可以使用,结果,找了好久,都没有找到合适的插件,有两个效果差强人意。 最后在回家的路上突然灵光一闪,想到了一种简单的实现方式,什么插件都不用,仅使用…...
Android widget 小部件使用指南强化版
Android widget 小部件使用指南强化版 一、简单UI的小部件二、含集合的小部件三、可配置的小部件四、可控制的小部件五、Android 12 Widget 更新 小部件是主屏幕定制的一个重要方面。您可以将它们视为应用程序最重要的数据和功能的“概览”视图,这些数据和功能可以直…...
Linux下C语言操作网卡的几个代码实例?特别实用
前面写了一篇关于网络相关的文章:如何获取当前可用网口。 《简简单单教你如何用C语言列举当前所有网口!》 那么如何使用C语言直接操作网口? 比如读写IP地址、读写MAC地址等。 一、原理 主要通过系统用socket()、ioctl()、实现 int sock…...
noip2011选择旅馆
1.审题:第一个人与第二个人入住的旅馆要求是同色的; 两个人去消费的旅馆并没有要求与入住的旅馆是同色的(这点要小心) 2.要求记录以下数据: 1)a[color]表示当前同为颜色color的旅馆数 2)b[co…...
vue造轮子完整指南--npm组件包开发步骤
一、项目包文件的创建和初始化。 1. 新建项目包。 vue create <Project Name> //用于发布npm包的项目文件名 ps:一般选择自定义,然后不需要Vuex和Router,其他选项按自己实际情况选择安装即可。 2.修改原始src文件名、新增组件项目存放文件和修改…...
28 drf-Vue个人向总结-1
文章目录 前后端分离开发展示项目项补充知识开发问题浏览器解决跨域问题 drf 小tips设置资源root目录使用自定义的user表设置资源路径media数据库补充删除表中数据单页面与多页面模式过滤多层自关联后端提交的数据到底是什么jwt token登录设置普通的 token 原理使用流程解析 jw…...
线性代数(七) 矩阵分析
前言 从性线变换我们得出,矩阵和函数是密不可分的。如何用函数的思维来分析矩阵。 矩阵的序列 通过这个定义我们就定义了矩阵序列的收敛性。 研究矩阵序列收敛性的常用方法,是用《常见向量范数和矩阵范数》来研究矩阵序列的极限。 长度是范数的一个特…...
myArm 全新七轴桌面型机械臂
引言 在不断演进的科技世界中,我们始终追求创新和卓越,以满足客户的需求并超越他们的期望。今天,我们很高兴地宣布我们的最新产品——myArm 300 Pi,一款七轴的桌面型机械臂。这款产品的独特之处在于其灵活性和可编程性,…...
tomcat乱码解决
解决乱码 1、修改bin\catalina.bat配置文件 修改tomcat的配置文件,找到tomcat路径下的\bin目录下的catalina.bat文件,修改 set “JAVA_OPTS%JAVA_OPTS% %JSSE_OPTS% -Dfile.encodingUTF-8 -Dsun.jnu.encodingUTF-8 ” 2、修改conf\logging.properties配置…...
【Linux】详解线程第三篇——线程同步和生产消费者模型
线程同步和生消模型 前言正式开始再次用黄牛抢票来讲解线程同步的思想通过条件变量来实现线程同步条件变量接口介绍初始化和销毁pthread_cond_waitsignal和broadcast 生产消费者模型三种关系用基本工程师思维再次理解基于生产消费者模型的阻塞队列版本一版本二多生多消 利用RAI…...
k8s 安装
文章目录 k8s 客户端安装k8s集群minikubekindkubeadm 验证 k8s 客户端 用于连接k8s集群,建议下载1.23.x的版本,其他的版本本地运行可能会有莫名其妙的报错 https://dl.k8s.io/release/v1.23.16/bin/linux/amd64/kubectl 安装k8s集群 minikube Minik…...
红队打靶:THE PLANETS: MERCURY打靶思路详解(vulnhub)
目录 写在开头 第一步:主机发现和端口扫描 第二步:Web渗透 第三步:获取初步立足点并搜集信息 第四步:软连接劫持sudo提权 总结与思考 写在开头 本篇博客在自己的理解之上根据大佬红队笔记的视频进行打靶,详述了…...
【网络协议】IP
当连接多个异构的局域网形成强烈需求时,用户不满足于仅在一个局域网内进行通信,他们希望通过更高一层协议最终实现异构网络之间的连接。既然需要通过更高一层的协议将多个局域网进行互联,那么这个协议就必须为不同的局域网环境定义统一的寻址…...
Python 布尔类型
布尔值表示两个值之一:True(真)或False(假)。 布尔值 在编程中,您经常需要知道一个表达式是否为True或False。 您可以在Python中评估任何表达式,并获得两个答案之一:True或False。…...
iOS设备管理器iMazing比iTunes好用吗?有哪些优势
虽然 iTunes 是 Apple 官方指定的 iPhone 数据备份和管理工具,但是一直以来 iTunes 卡顿的使用体验和过慢的备份过程为不少人诟病。如果大家也被 iTunes 体验不佳的备份和管理功能所困扰,那么简单易用、功能强大的iMazing 能为你解决这个问题。 iMazing…...
Opengl之深度测试
在坐标系统小节中,我们渲染了一个3D箱子,并且运用了深度缓冲(Depth Buffer)来防止被阻挡的面渲染到其它面的前面。在这一节中,我们将会更加深入地讨论这些储存在深度缓冲(或z缓冲(z-buffer))中的深度值(Depth Value),以及它们是如何确定一个片段是处于其它片段后方的。 …...
利用ICG-NH2/Amine进行DNA标记1686147-55-6星戈瑞
ICG-NH2(吲哚菁绿胺)可以用于DNA标记,这种标记方法通常涉及到DNA上的胺基反应基团和ICG-NH2之间的化学反应。以下是一种常见的方法,用于利用ICG-NH2标记DNA分子: 步骤: 1.准备目标DNA:你需要准…...
Pyecharts数据可视化
Pyecharts数据可视化 1、Pyecharts模块2、柱状图3、折线图4、饼图5、散点图6、图表合并7、词云8、地图 1、Pyecharts模块 ECharts是百度提供的基于JavaScript的开源可视化库,主要用于Web端数据可视化 Echarts是通过JS实现的,Pyecharts则可以使用Python来…...
集合-List集合
系列文章目录 1.集合-Collection-CSDN博客 2.集合-List集合-CSDN博客 文章目录 目录 系列文章目录 文章目录 前言 一 . 什么是List? 二 . List集合的特点 三 . 常用方法 1.void add(int index, E element): 将指定的元素插入到列表的指定位置。 2.E remove(int in…...
vuex的使用
1 vuex的使用 1 vuex的使用 store/index.js -在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式 的管理(读/写),也是一种组件间通信的方式,且适用于任意…...
raw图片处理软件:DxO PhotoLab 6 mac中文版支持相机格式
DxO PhotoLab 6 mac是一款专业的RAW图片处理软件,适用于Mac操作系统。它具有先进的图像处理技术和直观易用的界面,可帮助用户轻松地将RAW格式的照片转换为高质量的JPEG或TIFF图像。 DxO PhotoLab 6支持多种相机品牌的RAW格式,包括佳能、尼康、…...
ReactPortals传送门
ReactPortals传送门 React Portals提供了一种将子节点渲染到父组件以外的DOM节点的解决方案,即允许将JSX作为children渲染至DOM的不同部分,最常见用例是子组件需要从视觉上脱离父容器,例如对话框、浮动工具栏、提示信息等。 描述 <div&…...
【GDB】 command 命令
GDB command 命令 语法 command 命令是一个很好用的调试命令,它配合断点使用,可以在指定的断点执行预先设置的命令 其语法为:command bread_id,这样会提示你输入你要执行的命令,以 end 结束。这个 bread_id 就是用 …...
1038 统计同成绩学生
输入样例: 10 60 75 90 55 75 99 82 90 75 50 3 75 90 88 输出样例: 3 2 0 solution #include <stdio.h> int main(){int n, d, k, hash[101] {0}, a[100000];scanf("%d", &n);for(int i 0; i < n; i){scanf("%d&quo…...
北京礼品网站建设/网络营销的六大功能
入门三问: 组的概念是什么?为什么引入它?有什么用? 答:通过组可以更加方便的管理用户,组的概念应用于各行行业,例如企业会使用部门、职能或地理区域的分类方式来管理成员,映射在Li…...
长春网站建设加王道下拉/推广关键词优化公司
https://www.cnblogs.com/kumata/p/13304292.html...
网站设计用什么字体/朔州网站seo
Linux是目前应用最广泛的服务器操作系统,基于Unix,开源免费,由于系统的稳定性和安全性,市场占有率很高,几乎成为程序代码运行的最佳系统环境。linux不仅可以长时间的运行我们编写的程序代码,还可以安装在各…...
婚纱摄影网站首页/经典网络营销案例
二、操作系统应用41.请在考生文件夹下完成如下操作:(1)在考生文件夹下建立“计算机基础练习”文件夹;(2)在“计算机基础练习”文件夹下建立“文字”﹑“图片”和“多媒体”三个子文件夹;(3)在考生文件夹下查找一个名为“1.BMP”的图片文件,将它复制到“图…...
定制开发网站/无锡百度推广代理商
????????关注后回复 “进群” ,拉你进程序员交流群????????作者丨xiangzhihong来源:https://segmentfault.com/a/1190000039968136使用React Native开发移动App时,经常会遇到矢量图和自定义字体的开发需求,使用矢…...
做海报找背景图有哪些网站/优化网站排名推广
一、linux 系统内核参数 /etc/sysctl.conf文件常用参数 net.core.netdev_max_backlog 32768 #允许送到队列的数据包的最大数目 net.core.rmem_max 8388608 #SOCKET读缓存区大小net.core.wmem_max 8388608 #SOCKET写缓存区大小net.core.somaxconn 32768 …...