JavaEE之常见的锁策略

news2025/4/13 7:33:43

前面我们学习过线程不安全问题,我们通过给代码加锁来解决线程不安全问题,在生活中我们也知道有很多种类型的锁,同时在代码的世界当中,也对应着很多类型的锁,今天我们对锁一探究竟!

1. 常见的锁策略

注意: 接下来介绍的锁策略不仅仅是局限于Java.任何和"锁"相关的话题,都可能会涉及到以下内容.这些特性主要是给锁的实现者来参考的.
我们普通的程序猿也需要了解⼀些,对于合理的使用锁也是有很大帮助的.

1.1 乐观锁vs悲观锁

乐观锁: 在执行任务之前预期竞争不激烈,那就可以先不加锁,等后面如果真实发生了锁竞争再加锁

举个例子: 我很喜欢我的女友,在学校时,我们天天在一起,我不担心有冲突别人会占用她(并没有对她上锁),但是偶尔她会和她的好朋友出去,此时,我感觉到有人在和我竞争她,我就加锁,不让她离开我

悲观锁: 在执行任务之前预期竞争非常激烈(表示在执行任务的时候会发生锁冲突),必须先加锁再执行任务

举个例子: 我很喜欢我的女友,为了和她天天在一起,我对她上锁,这样当别人想要和她玩的时候,就会和我竞争,但是我持有锁,别人就会得不到她,显示她一直被我持有,哈哈哈

1.2 重量级锁vs轻量级锁

锁的核心特性"原子性",这样的机制追根溯源是CPU这样的硬件设备提供的.

  • CPU提供了"原子操作指令".
  • 操作系统基于CPU的原子指令,实现了mutex 互斥锁.
  • JVM基于操作系统提供的互斥锁,实现了synchronizedReentrantLock 等关键字和类.
    在这里插入图片描述

轻量级锁: 加锁机制尽可能不使用mutex,而是尽量在用户态代码完成.实在搞不定了,再使用mutex.加锁的过程比较简单,用到的资源比较少,典型就是用户态的一些操作(JAVA 层面就可以完成加锁)

举个例子: 有对象的男同胞,都会遇到的问题,就是和女友出去玩的时候,会等待女友的精心打扮(化妆),轻量级锁就是不停的自旋,一会问一下“宝宝,化好妆了吗”(只是一昧的催促女友,不干别的事情,消耗资源较少),可以第一时间知道女友啥时候化好妆可以出发

重量级锁: 加锁机制重度依赖了OS提供了mutex,加锁的过程比较复杂,用到的资源比较多,典型的就是内核态的一些操作

举个例子: 当女友在化妆的时候,我们不去过问,而是做好自己的事情,准备好出去玩的所有东西(一直在帮忙做事,消耗了很多资源),等待女友的召唤(唤醒),不能第一时间知道女友啥时候化好妆

理解用户态和内核态:
想象去银行办业务.
在窗口外,自己做,这是用户态.用户态的时间成本是比较可控的.
在窗口内,工作人员做,这是内核态.内核态的时间成本是不太可控的. 如果办业务的时候反复和工作人员沟通,还需要重新排队,这时效率是很低的.

乐观锁是能不加锁就不加锁,从而导致他干活少,消耗资源也少,所以可以说乐观锁就是一种轻量级锁
悲观锁是任何时候都加锁,从而导致他干活多,消耗资源也多,所以可以说悲观锁就是一种重量级锁

1.3 自旋锁vs挂起等待锁

自旋锁: 不停的检查锁是否被释放,如果一旦锁被释放就可以直接获取锁资源
挂起等待锁: 阻塞等待,等待到被唤醒

举个例子: 每周末,我要和我对象一起吃饭,我到了她宿舍楼下打电话问她啥时候下来,她说等一会,我就不停的打电话(一直在自旋,不停的检查锁的状态,她下楼了,我会第一时间发现)——>自旋锁,我不打电话了,然后去小亭子坐下来,等待(我就不能第一时间发现她下楼如果她下楼之后就需要喊我一声相当于通知我(唤醒),获取锁资源)——>阻塞:挂起等待锁

优缺点:

  1. 自旋锁:
    纯用户态的操作,可以第一时间获取到锁,
    有自旋次数和时间的限制,通过这个限制可以控制对系统资源的消耗,可以第一时间获取到锁
  2. 挂起等待锁:
    内核态的操作,会生成对应的加锁指令,要等待唤醒,在等待的过程中会释放CPU资源

自旋锁详情请看后续CAS详细介绍

1.4 公平锁vs非公平锁

公平锁: 先来后到,先排队的线程先拿到锁,后排队的线程后拿到锁,JAVA的JUC中有一个类专门实现了公平锁
非公平锁: 大家去抢,谁先抢到是谁的,synchronized是一个非公平锁

注意:

  • 一般情况下,大多数锁都是非公平锁!
  • 操作系统内部的线程调度就可以视为是随机的.如果不做任何额外的限制,锁就是非公平锁.如果要想实现公平锁,就需要依赖额外的数据结构,来记录线程们的先后顺序.
  • 公平锁和非公平锁没有好坏之分,关键还是看适用场景.

举个例子: 现实生活中如果要真正的公平:立法、执法、教育、环境都要发挥作用消耗更大的资源,实现公平锁的过程也是一样,需要用额外的逻辑去管理线程,做到先来后到

1.5.读写锁(readers-writerlock)

多线程之间,数据的读取方之间不会产生线程安全问题,但数据的写入方互相之间以及和读者之间都需要进行互斥。如果两种场景下都用同⼀个锁,就会产生极大的性能损耗。所以读写锁因此而产生。

⼀个线程对于数据的访问,主要存在两种操作:读数据和写数据.

  • 两个线程都只是读⼀个数据,此时并没有线程安全问题.直接并发的读取即可.
  • 两个线程都要写⼀个数据,有线程安全问题.
  • ⼀个线程读另外⼀个线程写,也有线程安全问题.

读写锁就是把读操作和写操作区分对待.Java标准库提供了ReentrantReadWriteLock 类,实现了读写锁.

  • ReentrantReadWriteLock.ReadLock 类表示⼀个读锁.这个对象提供了lock/unlock方法进行加锁解锁.
  • ReentrantReadWriteLock.WriteLock 类表示⼀个写锁.这个对象也提供了lock/unlock方法进行加锁解锁.

其中,

  • 读加锁和读加锁之间,不互斥.
  • 写加锁和写加锁之间,互斥.
  • 读加锁和写加锁之间,互斥.

注意,只要是涉及到"互斥",就会产⽣线程的挂起等待.⼀旦线程挂起,再次被唤醒就不知道隔了多久了.因此尽可能减少"互斥"的机会,就是提高效率的重要途径.
适用场景:
读写锁特别适合于"频繁读,不频繁写"的场景中.(这样的场景其实也是非常广泛存在的).

举个例子: 大学生必备学习通,老师会经常上课点名(读操作),发布学习资料(写操作),同学们看详细资料(读操作),读操作会涉及很多次,但是写操作偶尔几周一次,所以很适合读写锁

1.6可重入锁VS不可重入锁

可重入锁: 对一把锁可以连续加多次,多次加锁也要多次解锁,不造成死锁
不可重入锁: 对一把锁可以连续加多次,造成死锁

2. 相关面试题

2.1 你是怎么理解乐观锁和悲观锁的,具体怎么实现呢?

  1. 悲观锁认为多个线程访问同⼀个共享变量冲突的概率较大,会在每次访问共享变量之前都去真正加锁.
  2. 乐观锁认为多个线程访问同⼀个共享变量冲突的概率不大.并不会真的加锁,而是直接尝试访问数据.在访问的同时识别当前的数据是否出现访问冲突.
  3. 悲观锁的实现就是先加锁(比如借助操作系统提供的mutex),获取到锁再操作数据.获取不到锁就等待.
  4. 乐观锁的实现可以引入⼀个版本号.借助版本号识别出当前的数据访问是否冲突.

2.2 介绍下读写锁?

  1. 读写锁就是把读操作和写操作分别进行加锁.
  2. 读锁和读锁之间不互斥.
  3. 写锁和写锁之间互斥.
  4. 写锁和读锁之间互斥.
  5. 读写锁最主要用在"频繁读,不频繁写"的场景中

2.3 什么是自旋锁,为什么要使用自旋锁策略呢,缺点是什么?

  1. 如果获取锁失败,立即再尝试获取锁,无限循环,直到获取到锁为止.第⼀次获取锁失败,第二次的尝试会在极短的时间内到来.⼀旦锁被其他线程释放,就能第⼀时间获取到锁.

相比于挂起等待锁,

  1. 优点:没有放弃CPU资源,⼀旦锁被释放就能第⼀时间获取到锁,更高效.在锁持有时间比较短的场景下非常有用.
  2. 缺点:如果锁的持有时间较长,就会浪费CPU资源.

2.4 synchronized 是可重入锁么?

是可重入锁.
可重入锁指的就是连续两次加锁不会导致死锁.
实现的方式是在锁中记录该锁持有的线程身份,以及⼀个计数器(记录加锁次数).如果发现当前加锁的线程就是持有锁的线程,则直接计数自增.

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

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

相关文章

数字图像处理:实验二

任务一: 将不同像素(32、64和256)的原图像放大为像素大 小为1024*1024的图像(图像自选) 要求:1)输出一幅图,该图包含六幅子图,第一排是原图,第 二排是对应放大…

WEB渗透技术研究与安全防御

目录 作品简介I IntroductionII 1 网络面临的主要威胁1 1.1 技术安全1 2 分析Web渗透技术2 2.1 Web渗透技术的概念2 2.2 Web漏洞产生的原因2 2.3 注入测试3 2.3.1 注入测试的攻击流程3 2.3.2 进行一次完整的Sql注入测试4 2.3.3 Cookie注入攻击11 3 安全防御方案设计…

使用 Thermal Desktop 进行航天器热分析

介绍 将航天器保持在运行温度下的轨道上是一个具有挑战性的问题。航天器需要处理太空非常寒冷的背景温度,同时还要管理来自内部组件、地球反照率和太阳辐射的高热负荷。航天器在轨道上可以进行的各种轨道机动使解决这个问题变得更加复杂。 Thermal Desktop 是一款…

乘联会:1月汽车零售预计175万辆 环比暴跌33.6%

快科技1月18日消息,据乘联会的初步推算,2025年1月狭义乘用车零售总市场规模预计将达到约175万辆左右。与去年同期相比,这一数据呈现了-14.6%的同比下降态势;而相较于上个月,则出现了-33.6%的环比暴跌情况。 为了更清晰…

SQL 递归 ---- WITH RECURSIVE 的用法

SQL 递归 ---- WITH RECURSIVE 的用法 开发中遇到了一个需求,传递一个父类id,获取父类的信息,同时获取其所有子类的信息。 首先想到的是通过程序中去递归查,但这种方法着实孬了一点,于是想,sql能不能递归查…

【机器学习实战入门项目】使用深度学习创建您自己的表情符号

深度学习项目入门——让你更接近数据科学的梦想 表情符号或头像是表示非语言暗示的方式。这些暗示已成为在线聊天、产品评论、品牌情感等的重要组成部分。这也促使数据科学领域越来越多的研究致力于表情驱动的故事讲述。 随着计算机视觉和深度学习的进步,现在可以…

windows 搭建flutter环境,开发windows程序

环境安装配置: 下载flutter sdk https://docs.flutter.dev/get-started/install/windows 下载到本地后,随便找个地方解压,然后配置下系统环境变量 编译windows程序本地需要安装vs2019或更新的开发环境 主要就这2步安装后就可以了&#xff0…

【Linux】15.Linux进程概念(4)

文章目录 程序地址空间前景回顾C语言空间布局图:代码1代码2代码3代码4代码5代码6代码7 程序地址空间前景回顾 历史核心问题: pid_t id fork(); if(id 0) else if(id>0) 为什么一个id可以放两个值呢?之前没有仔细讲。 C语言空间布局图&am…

一文读懂服务器的HBA卡

什么是 HBA 卡 HBA 卡,全称主机总线适配器(Host Bus Adapter) ,是服务器与存储装置间的关键纽带,承担着输入 / 输出(I/O)处理及物理连接的重任。作为一种电路板或集成电路适配器,HBA…

oracle使用case when报错ORA-12704字符集不匹配原因分析及解决方法

问题概述 使用oracle的case when函数时,报错提示ORA-12704字符集不匹配,如下图,接下来分析报错原因并提出解决方法。 样例演示 现在有一个TESTTABLE表,本表包含的字段如下图所示,COL01字段是NVARCHAR2类型&#xff0…

Linux-----线程同步(条件变量)

目录 相关API restrict关键字 线程间条件切换函数 条件变量pthread_cond_t 案例 在前面的锁的基础上进一步提高线程同步效率,也就是两个线程只用锁去执行的话依然会存在资源竞争的情况,也就是抢锁,这里就需要在锁的这边加上限制&#xf…

每日进步一点点(网安)

今日练习题目是PHP反序列化,也学习一下说明是序列化和反序列化 1.PHP序列化 序列化是指将数据结构或对象转换为可传输或可储存的格式的过程。这通常需要将数据转换为字节流或者其他编码格式,以便在不同系统和应用程序之间进行传输或存储 在PHP中&…

Java-数据结构-二叉树习题(1)

对于二叉树的学习,主要的还是得多多练习~毕竟二叉树属于新的知识,并且也并不是线性结构,再加上经常使用递归的方法解决二叉树的问题,所以代码的具体流程还是无法看到的,只能通过画图想象,所以还是必须多加练…

(二)afsim第三方库编译(qt编译)

注意:源码编译的路径不能有中文否则报错,压缩包必须用官网下载的xz格式解压的才可以,否则sudo ./configure命令找不到 先编译openssl3.1.1软件包,否则编译的qt库将不支持network,相关库的编译(上文(一&…

【QT用户登录与界面跳转】

【QT用户登录与界面跳转】 1.前言2. 项目设置3.设计登录界面3.1 login.pro参数3.2 界面设置3.2.1 登录界面3.2.2 串口主界面 4. 实现登录逻辑5.串口界面6.测试功能7.总结 1.前言 在Qt应用程序开发中,实现用户登录及界面跳转功能是构建交互式应用的重要步骤之一。下…

【docker踩坑记录】

docker踩坑记录 踩坑记录(持续更新中.......)docker images 权限问题 踩坑记录(持续更新中…) docker images 权限问题 permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Head "http://%2Fvar%2Frun%2Fdocker.s…

搜维尔科技:Xsens人形机器人解决方案的优势

Xsens 致力于推动人形机器人技术的发展,塑造机器人与人类环境无缝融合的未来,通过创新精确和协作,协助生产和服务,改善人类生活和产业。 Xsens通过人形跟随捕捉详细的人体运动数据,使机器人能够学习类人的动作&#x…

国内微电子(集成电路)领域重点高校的特色与优势

本文旨在梳理国内微电子(集成电路)领域重点高校的特色与优势,为有志于从事相关领域的学生提供参考。文章将从学科特色、科研实力(以ISSCC论文为参考之一)、行业认可度等方面进行分析,并强调实验室、导师、研…

leetcode707-设计链表

leetcode 707 思路 本题也是用了虚拟头节点来进行解答,这样的好处是,不管是头节点还是中间的节点都可以当成是中间节点来处理,用同一套方法就可以进行处理,而不用考虑太多的边界条件。 下面题目中最主要的实现就是添加操作addA…

数据结构-栈队列OJ题

文章目录 一、有效的括号二、用队列实现栈三、用栈实现队列四、设计循环队列 一、有效的括号 (链接:ValidParentheses) 这道题用栈这种数据结构解决最好,因为栈有后进先出的性质。简单分析一下这道题:所给字符串不是空的也就是一定至少存在一…