面试题:说一说多线程常见锁的策略 ?

文章目录

  • 前言
  • 一、乐观锁和悲观锁
    • 1.1 定义
    • 1.2 生动有趣滴例子
    • 1.3 版本号机制
  • 二、读写锁
    • 2.1 读写锁的由来
    • 2.2 生动有趣de例子
    • 2.3 ReentrantReadWriteLock 类
  • 三、重量级锁与轻量级锁
    • 3.1 定义
    • 3.2 生动活泼の例子
    • 3.3 自旋锁(Spin Lock)
  • 四、公平锁与非公平锁
  • 五、可重入锁和不可重入锁


前言

所谓锁的策略就是指如何实现锁。Java、MySQL、Go、C++等等都有类似的锁策略。


一、乐观锁和悲观锁

这两种锁都有相应的应用场景。

1.1 定义

乐观锁:

每次读写数据都认为不会发生冲突,线程不会阻塞,一般来说,只有在进行数据更新时才会检查是否发生冲突,若没有冲突,直接更新,只有冲突(多个线程都在更新数据)了才解决冲突问题。

当线程冲突不严重的时候,可以采用乐观锁策略来避免多次的加锁解锁操作。

悲观锁:

每次去读写数据都会冲突,每次在进行数据读写时都会上锁(互斥),保证同一时间段只有一个线程在读写数据。

当线程冲突严重时,就需要加锁,来避免线程频繁访问共享数据失效带来的CPU空转问题。

1.2 生动有趣滴例子

举个栗子:

悲观锁策略:

每次你(线程)跑来找我(线程或者资源)都认为我忙着呢,先给我发个消息 “嗨嗨嗨,VIBE在吗?”(尝试加锁),我没回或者回了个”忙着呢“,你就得等待(线程阻塞),一直等到我回复你”我好了“ (CPU唤醒了等待线程,尝试重新加锁),此时你被唤醒,对我加锁,我们就可以愉快聊天了~

乐观锁策略:

你认为每次找我的时候,我都闲着呢,直接就找我发消息要请我吃火锅(不上锁,直接访问数据),若我确实闲着呢(直接响应,避免了加锁和解锁的操作),如果我此时忙着呢,我就一直不回复,你一看我没及时回复你,你就跑去干别的事情了(线程不会阻塞,去干别的事情),过段时间再来。

乐观锁不是真的把线程阻塞了。乐观锁的实现一般都会采用版本号机制来实现~

1.3 版本号机制

乐观锁的一个重要功能就是要检测出数据是否发生访问冲突,我们可以引入一个”版本号“来解决。

一般锁的实现都是乐观锁和悲观锁并用的策略。
synchronized最开始就是乐观锁,当竞争激烈再升级为悲观锁。
下面博主将画图详细讲解版本号机制:

(1) 线程1和2从主内存读取到数据到自己的工作内存中,此时版本号都是 ”1“。

图片

(2)线程1把自己的V值改成30,线程2把自己的V值改成70

图片

(3)假如线程1先完成修改,将数据版本号+1(version = 2),然后一起写回主内存

图片

(4)此时线程2想更新自己的工作内存值到主内存,发现不满足”提交版本必须大于记录当前版本才能执行更新“的乐观锁策略,就认为这次写回失败。

图片

(5) 线程2写入失败,就从主存中读取最新的值和版本号到自己工作内存中,然后尝试在最新的数据上进行操作,若最后写回成功,主存和工作内存的值+1,否则执行CAS策略,不断重试写回,直到成功为止。

二、读写锁

2.1 读写锁的由来

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

读写锁特别适用于线程基本都在读数据,很少有写数据的情况。

多线程访问数据时,并发读取数据不会有线程安全问题,只有在更新数据(增删改)时会有线程安全问题,将锁分为读锁和写锁。

  • 多个线程并发访问读锁(读数据),则多个线程都能访问到数据,读锁和读锁是并发的,不互斥
  • 两个线程都需要访问写锁(写数据),则这两个线程互斥,只有一个线程能成功获取到写锁,其他线程阻塞
  • 当一个线程读,另一个线程写(也互斥,只有当写线程结束时,读线程才能继续执行)

注意, 只要是涉及到 “互斥”, 就会产生线程的挂起等待. 一旦线程挂起, 再次被唤醒就不知道隔了多久了.

因此尽可能减少 “互斥” 的机会, 就是提高效率的重要途径

2.2 生动有趣de例子

举个栗子:

比如大家都看过的网文,作者在码字的时候,所有读者都得等作者写完,才能读。

2.3 ReentrantReadWriteLock 类

synchronized不是读写锁,JDK内置了另一个ReentrantReadWriteLock实现读写锁

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

三、重量级锁与轻量级锁

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

  1. CPU 提供了 “原子操作指令”.
  2. 操作系统基于 CPU 的原子指令, 实现了 mutex 互斥锁.
  3. JVM 基于操作系统提供的互斥锁, 实现了 synchronized 和 ReentrantLock 等关键字和类.

3.1 定义

重量级锁:
需要操作系统和硬件支持,线程获取重量级锁失败进入阻塞状态(os,用户态切换到内核态,开销非常大)

轻量级锁:

尽量在用户态执行操作,线程不阻塞,不会进行状态切换。

3.2 生动活泼の例子

举个栗子:

假如此时要去银行办理业务,窗口外部自己处理的业务就属于用户态,窗口内部需要工作人员协助的就处于内核态

重量级锁: 若某个业务涉及到赚钱打款,就要频繁切换用户态和内核态,非常耗时。

轻量级锁: 此时可以把这些操作业务都放在用户态解决

3.3 自旋锁(Spin Lock)

按之前的方式,线程在抢锁失败后进入阻塞状态,放弃 CPU,需要过很久才能再次被调度.

但实际上, 大部分情况下,虽然当前抢锁失败,但过不了很久,锁就会被释放。没必要就放弃 CPU. 这个时候就可以使用自旋锁来处理这样的问题.

轻量级锁的常用实现就是采用自旋锁

自旋锁就是循环,以下是伪代码:

while (获取(lock) == false) {//循环}

线程获取锁失败并不会让出CPU,线程也不阻塞,不会从用户态切换到内核态,线程在CPU上空跑,当锁被释放,此时这个线程很快就会获取到锁。

举个栗子:

比如等红绿灯:

  • 如果每次等都熄火,当绿灯再打火启动,这就是挂起等待锁
  • 如果每次发动机不熄火,踩着刹车,等绿灯亮了可以直接走,这就是自旋锁

四、公平锁与非公平锁

公平锁:
获取锁失败的线程进入阻塞队列,当锁被释放,第一个进入队列的线程首先获取到锁(等待时间最长的线程获取到锁)

非公平锁:

获取锁失败的线程进入阻塞队列,当锁被释放,所有在队列中的线程都有机会获取到锁,获取到锁的线程不一定就是等待时间最长的线程

  • synchronized锁就是非公平锁
  • ReentrantLock默认是非公平锁,可以在构造方法中传入true开启公平锁

五、可重入锁和不可重入锁

可重入锁的字面意思是“可以重新进入的锁”,即允许同一个线程多次获取同一把锁。

举个栗子:

比如一个递归函数里有加锁操作,递归过程中这个锁会阻塞自己吗?如果不会,那么这个锁就是可重入锁(因为这个原因可重入锁也叫做递归锁)。

Java里只要以Reentrant开头命名的锁都是可重入锁,而且JDK提供的所有现成的Lock实现类,包括synchronized关键字锁都是可重入的。

而 Linux 系统提供的 mutex 是不可重入锁。

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

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

相关文章

龙芯3A5000上使用腾讯会议

原文链接:龙芯3A5000上使用腾讯会议 hello,大家好啊!今天我要给大家介绍的是在龙芯3A5000处理器上安装使用腾讯会议的经验分享。随着远程工作和在线会议的普及,腾讯会议成为了许多人日常工作不可或缺的工具。而对于使用龙芯3A5000…

数据洞察力,驱动企业财务变革

我们不得不面对一个现实,就是数据量的剧增。加上大部分企业并不愿意删除历史数据,以防未来预测分析时需要,这造成数据就像一个雪球,越滚越大。然而,过多的数据和数据不足一样会成为企业发展和理解分析的障碍。从海量数…

2024.01.09.Apple_UI_BUG

我是软件行业的,虽然不是手机设计的,但是这个设计真的导致经常看信息不完整,要下拉的。 特别读取文本或者其他文件的时候,上面有个抬头就是看不到,烦,体验感很差

统信UOS_麒麟KYLINOS与Windows通过Open SSH实现文件传输

原文链接:统信UOS/麒麟KYLINOS与Windows通过Open SSH实现文件传输 hello,大家好啊!今天我要给大家介绍的是在统信UOS或麒麟KYLINOS操作系统与Windows系统之间通过Open SSH实现文件传输的方法。在日常工作中,我们经常需要在不同操作…

vue v-for循环拖拽排序,实现数组选中的数据拖拽后对应的子数据也进行重新排序

如下图所有,有个需求更新, 实现拖拽。 1,当新增了测点类型的时候每个对应的回路子数据都会新增对应的测点类型。 2,当拖动测点类型结束的时候对应的回路里面的内容也会跟着测点类型的排序自动排序 其实很简单,只要会了…

java方法的定义和使用

方法 今日目标: 能够知道方法的好处 能够根据两个明确分析方法的参数和返回值 能够编写方法完成授课案例,并在主方法中完成方法的调用 能够知道方法重载及其特点 1:方法的定义和使用 1.1 方法概述 方法(method):就是完成特…

【Flet教程】使用Flet以Python创建TODO应用程序

Flet是基于Python实现的Flutter图形界面GUI。除了使用Python,具备美观、简洁、易用,还有Flutter本身的跨平台(安卓、iOS、Win、Mac、Web)、高性能、有后盾的特点。下面是0.18版官方TODO APP教程,为了准确,保…

大数据技术原理与应用 期末复习 知识点全总结(林子雨版

目录 1.第一章 大数据概述:(一)三次信息化浪潮(二)人类社会数据产生方式的3个阶段(三)大数据的3个发展阶段(四)大数据4V概念(五)数据存储单位之间…

基于Github官方教程的快速入门学习

GitHub 是一个用于版本控制和协作的代码托管平台。 它允许您和其他人随时随地协同处理项目。 创建仓库 在任何页面的右上角,使用 下拉菜单选择“新建存储库”。 之后会进入创建仓库的界面,需要我们进行如下操作: 写仓库的名字写对于本仓库…

BERT 模型是什么

科学突破很少发生在真空中。相反,它们往往是建立在积累的人类知识之上的阶梯的倒数第二步。要了解 ChatGPT 和 Google Bart 等大型语言模型 (LLM) 的成功,我们需要回到过去并谈论 BERT。 BERT 由 Google 研究人员于 2018 年开发&…

代码随想录算法训练营第27天 | 39. 组合总和 40.组合总和II 131.分割回文串

目录 39. 组合总和 💡解题思路 💻实现代码 40.组合总和II 💡解题思路 💻实现代码 131.分割回文串 💡解题思路 # 判断回文子串 💻实现代码 39. 组合总和 题目链接:39. 组合总和 给定…

C++OpenCV学习笔记(0):从开始到放弃

文章目录 前言环境配置Hello worldC 和C# 语法对比模板字符串list列表 总结 前言 作为一个计算机本科学生,我大学的时候深深的被指针和内存管理给折磨过。我深刻的理解内存泄漏的巨大问题。但是我最近学习Python的时候发现,Python是真的不好进行项目管理…

回归预测 | Matlab基于SO-GRU蛇群算法优化门控循环单元的数据多输入单输出回归预测

回归预测 | Matlab基于SO-GRU蛇群算法优化门控循环单元的数据多输入单输出回归预测 目录 回归预测 | Matlab基于SO-GRU蛇群算法优化门控循环单元的数据多输入单输出回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab基于SO-GRU蛇群算法优化门控循环单元的数…

分布式I/O应用于智慧停车场的方案介绍

客户案例背景 目前车位检测技术有磁电技术、超声波技术、红外线技术、图像识别车位技术。考虑到例如电磁干扰、信号干扰等的环境因素影响,通常会采用组合使用的方式进行,如采用不同的传感器、应用不同的协议等,以便提高车位检测的准确性和实时…

鼠标随动指定区域高亮显示(Excel聚光灯)

实例需求:工作表中数据表实现跟随鼠标选中高亮效果,需要注意如下几个细节需求 数据表为连续区域,但是不一定从A1单元格开始数据表的前两行(标题行)不使用高亮效果数据表中已经应用了条件格式,高亮显示取消…

Docker 安装部署

1、Docker 安装 ① 卸载docker,清空之前的docker文件 yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-selinux \docker-engine-selinux \docker-engine \docker-ce…

AI Table应用程序接口表的格式说明和作用

AI Table 首先全拼不是AI人工智能表,而是Application Interface Table应用程序接口表。此表按照AUTOSAR的格式规范去定义,并且使用此Excel 表格生成相应的应用软件组件Arxml文件。下面就让我们按照AUTOSAR_EXP_AIUserGuide.pdf文档官方解释描述文件去看看…

系统存储架构升级分享 | 京东云技术团队

一、业务背景 系统业务功能:系统内部进行数据处理及整合, 对外部系统提供结果数据的初始化(写)及查询数据结果服务。 系统网络架构: 部署架构对切量上线的影响 - 内部管理系统上线对其他系统的读业务无影响分布式缓存可进行单独扩容, 与存储及查询功能升级无关通过…

编译ZLMediaKit(win10+msvc2019_x64)

前言 因工作需要,需要ZLMediaKit,为方便抓包分析,最好在windows系统上测试,但使用自己编译的第三方库一直出问题,无法编译通过。本文档记录下win10上的编译过程,供有需要的小伙伴使用 一、需要安装的软件…

C# 图解教程 第5版 —— 第23章 异常

文章目录 23.1 什么是异常23.2 try 语句23.3 异常类23.4 catch 子句23.5 异常过滤器23.6 catch 子句段23.7 finally 块23.8 为异常寻找处理程序23.9 进一步搜索23.9.1 一般法则23.9.2 搜索调用栈的示例(*) 23.10 抛出异常23.11 不带异常对象的抛出23.12 …