当前位置: 首页 > news >正文

【多线程】乐观/悲观锁、重量级/轻量级锁、挂起等待/自旋锁、公平/非公锁、可重入/不可重入锁、读写锁

文章目录

  • 乐观锁和悲观锁
  • 重量级锁和轻量级锁
  • 挂起等待锁和自旋锁
  • 公平锁和非公平锁
  • 可重入锁和不可重入锁
  • 读写锁
  • 相关面试题

:非常广义的概念,不是指某个具体的锁,所有的锁都可以往这些策略中套
synchronized:只是市面上五花八门的锁种,其中一种典型的实现,Java 内置的,推荐使用的锁

乐观锁和悲观锁

这两个词不是指某个具体的锁,而是锁的一种“特性”,描述了“一类”

乐观锁:加锁的时候,假设出现冲突的概率不大

  • 接下来围绕加锁要做的工作就会更少
    悲观锁:加锁的时候,假设出现锁冲突的概率很大
  • 接下来围绕加锁要做的工作就会更多

使用 synchronized,初始情况下,是乐观的(预估接下来锁冲突概率不大)

  • 同时会在背后偷偷地统计锁冲突了多少次
  • 如果发现锁冲突达到一定程度了,就会转变为“悲观的”
    乐观锁和悲观锁需要做的事情是不同的
  • 乐观做的事情少一点
  • 悲观做的事情往往更重量级

站在预测锁冲突的概率是否高
synchronized 是自适应的

重量级锁和轻量级锁

效果和悲观乐观是重叠的,只是站在的角度不一样

重量级锁:加锁的开销比较大,要做的工作更多

  • 往往悲观的时候,会做的
    轻量级锁:加锁的开销比较小,要做的工作相对更少
  • 往往乐观的时候,会做的
    但也不能认为是 100%等价,因为:
  • 乐观和悲观是站在“预估所冲突”角度
  • 重量轻量是站在“加锁开销“角度

站在加锁的开销角度
synchronized 也是自适应的

挂起等待锁和自旋锁

挂起等待锁:属于是悲观锁/重量级锁的一种典型实现
自旋锁:乐观锁/轻量级锁的一种典型实现

比如:
你去追你的女神:
女神女神,我好喜欢你,

  • 你尝试对女神加锁
    女神表示:我有男朋友了

  • 女神表示她这把锁已经被别的线程给加了
    你就可以选择“等待”,等到女神锁被释放,比如:

  • 你选择每天仍然会给女神不停地问候“早安,午安…”,这里的行为称为“自旋锁

    • 这里的等待是“忙等”,等待的过程中 CPU 的资源不会释放
    • 某天女神和男朋友吵架了,不开心,你就立刻能感知到,机会来了。这样你就可以在女神锁释放的第一时间,立刻抓住机会,能够上位
    • 不停地,循环地检测锁是否被释放,一旦锁释放,就能立即有机会能获得锁
  • 你选择把女神拉黑,先不联系了,若干年后你从别人那里听说,女神分手了,你再去联络女神,这种行为就是“挂起等待锁

    • 不联系,就相当于“让出 CPU 资源”CPU 就可以去做别的事了
    • 不理女神之后,我们就可以有心思好好学习,好好敲代码,好好找工作了,在过程中做成更多的事情。过了一段时间后,我们通过一些途径听说女神分手了,再伺机而动,但“听说”的时效性很低,这个中间可能有很长的时间跨度。在这个时间跨度里,女神是否由谈了男朋友?分手了多少次?你是不知道的
    • 挂机时间更长,但能节省下 CPU 资源去做别的事情

注意:

  • 只有在假定锁冲突概率不高的情况下,才能“忙等”。如果好几个线程都在竞争同一个锁,一个线程拿到锁,其他的都在“忙等”,这样总的 CPU 消耗就会非常高
  • 而且,由于竞争太激烈,导致有些线程要等待很久才能拿到锁
  • 锁冲突很高的概率很高的话就不适合“自旋锁”方案

  • 挂起等待锁也就适合“悲观锁”这样的场景了
  • 锁竞争非常激烈,预测拿到锁的概率本身就不打,不放吧 CPU 让出来,充分的做其他事情

synchronized“自适应”

  • 轻量级锁就是基于自旋的方式实现的(JVM 内部,用户态代码实现)
  • 重量级锁就是基于挂起等待的方式实现的(调用操作系统 API,在内核中实现)
    纯用户态代码往往执行效率比内核态代码的高一些,总体来说,我们还是认为“自旋”的效率更高的,但是 CPU 开销更大
    挂起等待锁的操作虽然 CPU 开销变少了,但整体的等待时间更多

公平锁和非公平锁

日常生活中,说的公平可能有不同的含义


image.png|521
当女神分手了,该轮到谁上位呢?

公平锁: 在计算机中,约定“先来后到”为公平
image.png|556


非公平锁: 系统原生
image.png|365


synchronized 属于非公平锁

  • N 个线程竞争同一个锁,其中一个线程拿到锁了,后续该线程释放锁之后,剩下的 N-1 个线程,就要重新竞争锁,谁能拿到锁,就不一定了
  • 当然,也不能保证这些线程竞争中获取的概率一定是数学上的严格均等
    本身操作系统内核里针对锁的处理就是如此,synchronized 在系统内核的基础上,没有做啥额外的操作

如需要使用公平锁,就需要做额外的工作

  • 比如引入队列,记录每个线程加锁的顺序

可重入锁和不可重入锁

死锁问题:如果一个线程,针对同一把锁,连续加锁两次,就可能出现死锁,如果把锁设为“可重入”就可以避免死锁了

可重入:是专门的计算机术语,不要写作“可重复”这样的词

可重入锁

  • 会记录当前是哪个线程持有了这把锁
  • 在加锁的时候判定,当前申请锁的线程,是否就是锁的持有者
  • 计数器,记录加锁的次数,从而确定何时真正释放锁
    • 遇到一个 { 加一次锁,计数器加一
    • 遇到一个 } 解锁一次,计数器减一
    • 等到计数器为零,真正释放锁

读写锁

synchronized 并非是读写锁
所谓的读写锁,把“加锁操作”分为两种情况

  • 读加锁
  • 写加锁
    如果多个线程同时读这个变量,没有线程安全问题,但是
  • 一个线程读/一个线程写
  • 两个线程都写
    就会产生问题

在实际开发中,在大部分场景下,读操作的频次,本身就比写操作的频次高。所以就让读操作不产生锁冲突,这样就只有少数写操作会产生冲突,这样效率就高了


读写锁提供了两种加锁的 API,系统内置的锁,Java 标准库中的读写锁类为:ReentrantReadWriteLock

  • 加读锁
    • ReentrantReadWriteLock 的内部类 ReentrantReadWriteLock.ReadLock 表示加读锁,这个对象提供了 lock/unlock ⽅法进⾏加锁解锁
  • 加写锁
    • ReentrantReadWriteLock 的内部类 ReentrantReadWriteLock.WriteLock 表示加写锁,这个对象也提供了lock/unlock ⽅法进⾏加锁解锁
      解锁的 API 是一样的,就需要把 unlock 放到 finally 中,确保能够执行到
  • 如果两个线程都是按照读方式加锁,此时不会产生锁冲突;
  • 如果两个线程都是家写锁,此时会产生锁冲突
  • 如果一个线程读锁,一个线程写锁,也会产生冲突

相关面试题

image.png


image.png


image.png


image.png

相关文章:

【多线程】乐观/悲观锁、重量级/轻量级锁、挂起等待/自旋锁、公平/非公锁、可重入/不可重入锁、读写锁

文章目录 乐观锁和悲观锁重量级锁和轻量级锁挂起等待锁和自旋锁公平锁和非公平锁可重入锁和不可重入锁读写锁相关面试题 锁:非常广义的概念,不是指某个具体的锁,所有的锁都可以往这些策略中套 synchronized:只是市面上五花八门的锁…...

31_逻辑漏洞、水平垂直越权、垂直越权漏洞测试、水平越权

概述 如果使用A用户的权限去操作B用户的数据,A的权限小于B的权限,如果能够成功操作,则称之为越权操作。 越权漏洞形成的原因是后台使用了 不合理的权限校验规则导致的。 一般越权漏洞容易出现在权限页面(需要登录的页面&#xff0…...

css写一个按钮流光动画效果

规则说明 按钮实现一个简易的流光动画 streamer.css.pay_button{width: 281*2px;height: 104px;border-radius: 80px;color: rgba(255, 255, 255, 1);background: linear-gradient(90deg, #FFA023 0%, #FF2B87 100%);margin-bottom: 20px;font-size: 32px;position: relative;o…...

AxMath保姆级安装教程(word联用)及使用TIPS

一、软件介绍 AxMath是一款数学公式编辑器软件。它提供了一个直观的界面,使用户可以轻松创建和编辑数学公式。AxMath支持多种数学符号、方程式、函数、矩阵等的输入和编辑,并提供了丰富的数学符号库和模板,方便用户快速创建复杂的数学公式。…...

Vue-03.指令-v-on

v-on 为HTML标签绑定事件 代码演示: 在下面的代码中,在input标签中定义了一个按钮,并且使用v-on为input标签绑定了一个事件click,当鼠标点击该按钮时,会触发指定的方法handle,如果…...

接口基础知识6:详解http request body(一篇讲完常见请求体)

课程大纲 一、定义 HTTP请求体(HTTP Request body):HTTP请求消息的可选部分,仅在请求方法支持且需要发送数据时使用。 POST方法、PUT方法有请求体,GET和HEAD方法没有请求体。 请求头和请求体之间会有一个空行&#…...

Windows Server 安装Web,DHCP,DNS,FTP四大服务及其配置和监控方式

以Windows Server 2022为例 1.安装Web服务 添加角色->Web服务器勾选 添加Web服务器后会有一个interpub文件夹,其中wwwroot为网站根目录 工具->IIS->网站 ,这里可以将风险服务停掉 2.安装DHCP服务 添加角色->DHCP服务器勾选 右击IPv4->新建作用域 …...

创意指南丨VR游览沉浸式空间体验

欢迎来到我们制作的VR幻想世界。玩家的起点是一条蓝色水晶大道,让我们一起探索这个如梦似幻的境地。 在这条大道的两侧,漂浮着半透明的大水晶水母。它们轻盈地在空中飘动,仿佛在欢迎我们的到来。这条道路上方,一个个半圆环不停地…...

【iOS】—— autoreleasePool以及总结

【iOS】—— autoreleasePool以及总结 1. 什么是autoreleasePool2. autoreleasePoolPageobjc_autoreleasePoolPush方法:objc_autoreleasePoolPop方法:tokenkill()方法 3. 总结3.1 autoreleasePool的原理3.2 autoreleasePool的问题3.2.1 autoreleasepool的…...

培训第二十五天(python中执行mysql操作并将python脚本共享)

mysql下载路径&#xff1a; MySQL :: MySQL Community Downloads [root2 ~]# vim py001.pya3b4print(ab)print(a**2b**2)[root2 ~]# python py001.py 725[root2 ~]# python3>>> import random>>> random<module random from /usr/lib64/python3.6/random…...

LVS实战项目

LVS简介 LVS:Linux Virtual Server&#xff0c;负载调度器&#xff0c;内核集成&#xff0c;章文嵩&#xff0c;阿里的四层SLB(Server LoadBalance)是基于LVSkeepalived实现。 LVS集群的类型 lvs-nat &#xff1a; 修改请求报文的目标IP, 多目标 IP 的 DNAT lvs-dr &#xff…...

笔记小结:《利用python进行数据分析》之层次化索引

层次化索引 导入样例 层次化索引&#xff08;hierarchical indexing&#xff09;是pandas的一项重要功能&#xff0c;它使你能在一个轴上拥有多个&#xff08;两个以上&#xff09;索引级别。抽象点说&#xff0c;它使你能以低维度形式处理高维度数据。我们先来看一个简单的例…...

Linux的线程篇章 - 线程池、进程池等知识】

Linux学习笔记---018 Linux之线程池、进程池知识储备1、线程池1.1、池化技术1.1.1、定义与原理1.1.2、优点1.1.3、应用场景 1.2、线程池的特点与优势1.3、线程池的适用场景1.4、线程池的实现 2、进程池2.1、定义和基本概念2.2、进程池的特点与优势2.3、进程池的适用场景&#x…...

汇昌联信做拼多多运营正规吗?

汇昌联信在拼多多平台上的运营是否正规&#xff0c;是许多商家和消费者都关心的问题。随着电商行业的快速发展&#xff0c;平台运营的正规性直接关系到消费者的购物体验和商家的信誉。 一、公司背景与资质审核 明确回答问题&#xff1a;汇昌联信作为一家专业的电商运营公司&…...

240810-Gradio自定义Button按钮+事件函数+按钮图标样式设定

A. 最终效果 B. 参考代码 要通过自定义HTML按钮来触发Gradio自带按钮的 click 函数&#xff0c;你可以使用JavaScript来模拟点击Gradio的按钮。这里是一个示例代码&#xff0c;展示了如何实现这一点&#xff1a; import gradio as gr# 自定义的 JavaScript&#xff0c;用于捕…...

排序算法--快速排序

一、三色旗问题 问题描述 有一个数组是只由0&#xff0c;1&#xff0c;2三种元素构成的整数数组&#xff0c;请使用交换、原地排序而不是计数进行排序&#xff1a; 解题思路 1.定义两个变量&#xff0c;i和j&#xff08;下标&#xff09;&#xff0c;从index0开始遍历 2.如…...

springMVC -- 学习笔记

前言简介演示 ⇒ &#xff08;最简单的原理&#xff0c;开发并不这样&#xff0c;理解一下就好&#xff09;演示 ⇒ 接近真实注解开发&#xff08;好好理解一下&#xff09;重要的源码献上 Controller 讲解RequestMapper ⇒ 没啥记的&#xff0c;第一个案例看看就行了RestFul 风…...

修复本地终端(windows)连接服务器使用zsh出现乱跳的问题

目前市面上还没有发现解决方案&#xff0c;记录一下&#xff01; 1.起因&#xff1a; 在服务器配置了zsh后&#xff0c;用本地的windows去连接的时候&#xff0c;终端内容会出现乱跳&#xff0c;比如输入了一个“l”&#xff0c;后面出现多个“lll”&#xff0c;如下: ⚡ roo…...

【扒代码】regression_head.py

import torch from torch import nnclass UpsamplingLayer(nn.Module):# 初始化 UpsamplingLayer 类def __init__(self, in_channels, out_channels, leakyTrue):super(UpsamplingLayer, self).__init__() # 调用基类的初始化方法# 初始化一个序列模型&#xff0c;包含卷积层、…...

vue2 使用axios 请求后台返回文件流导出为excel

目录 步骤 1: 安装 Axios 步骤 2: 创建 Axios 实例 步骤 3: 发起请求并处理文件流 说明 步骤 1: 安装 Axios 首先&#xff0c;确保项目中已经安装了 Axios。如果没有&#xff0c;可以通过以下命令进行安装&#xff1a; npm install axios 步骤 2: 创建 Axios 实例 为了更…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

R 语言科研绘图第 55 期 --- 网络图-聚类

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…...

day36-多路IO复用

一、基本概念 &#xff08;服务器多客户端模型&#xff09; 定义&#xff1a;单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用&#xff1a;应用程序通常需要处理来自多条事件流中的事件&#xff0c;比如我现在用的电脑&#xff0c;需要同时处理键盘鼠标…...

go 里面的指针

指针 在 Go 中&#xff0c;指针&#xff08;pointer&#xff09;是一个变量的内存地址&#xff0c;就像 C 语言那样&#xff1a; a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10&#xff0c;通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...

C++--string的模拟实现

一,引言 string的模拟实现是只对string对象中给的主要功能经行模拟实现&#xff0c;其目的是加强对string的底层了解&#xff0c;以便于在以后的学习或者工作中更加熟练的使用string。本文中的代码仅供参考并不唯一。 二,默认成员函数 string主要有三个成员变量&#xff0c;…...

Copilot for Xcode (iOS的 AI辅助编程)

Copilot for Xcode 简介Copilot下载与安装 体验环境要求下载最新的安装包安装登录系统权限设置 AI辅助编程生成注释代码补全简单需求代码生成辅助编程行间代码生成注释联想 代码生成 总结 简介 尝试使用了Copilot&#xff0c;它能根据上下文补全代码&#xff0c;快速生成常用…...

Linux信号保存与处理机制详解

Linux信号的保存与处理涉及多个关键机制&#xff0c;以下是详细的总结&#xff1a; 1. 信号的保存 进程描述符&#xff08;task_struct&#xff09;&#xff1a;每个进程的PCB中包含信号相关信息。 pending信号集&#xff1a;记录已到达但未处理的信号&#xff08;未决信号&a…...

分布式计算框架学习笔记

一、&#x1f310; 为什么需要分布式计算框架&#xff1f; 资源受限&#xff1a;单台机器 CPU/GPU 内存有限。 任务复杂&#xff1a;模型训练、数据处理、仿真并发等任务耗时严重。 并行优化&#xff1a;通过任务拆分和并行执行提升效率。 可扩展部署&#xff1a;适配从本地…...