【多线程】常见的锁策略

news2024/10/2 10:41:45

请添加图片描述

✨个人主页:bit me👇
✨当前专栏:Java EE初阶👇
✨每日一语:老当益壮,宁移白首之心;穷且益坚,不坠青云之志。

目 录

  • 🏳️一. 乐观锁 vs 悲观锁
  • 🏴二. 普通的互斥锁 vs 读写锁
  • 🏁三. 重量级锁 vs 轻量级锁
  • 🚩四. 自旋锁 vs 挂起等待锁
  • 🏳️‍🌈五. 公平锁 vs 非公平锁
  • 🏴‍☠️六. 可重入锁 vs 不可重入锁

锁策略:加锁的时候咋加的


🏳️一. 乐观锁 vs 悲观锁

  • 悲观锁总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。
  • 乐观锁假设数据一般情况下不会产生并发冲突,所以在数据进行提交更新的时候,才会正式对数据是否产生并发冲突进行检测,如果发现并发冲突了,则让返回用户错误的信息,让用户决定如何去做。

Synchronized 就既是一个悲观锁,也是一个乐观锁,是一种自适应锁。当前锁冲突概率不大,以乐观锁方式运行,往往是纯用户态执行的,一旦发现锁冲突概率大了,以悲观锁的方式运行,往往要进入内核,对当前线程进行挂起等待。


🏴二. 普通的互斥锁 vs 读写锁

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

  • Synchronized就属于普通的互斥锁,两个加锁操作之间会发生竞争
  • 读写锁,把加锁操作细化了,加锁分成了 “加读锁” “加写锁”。

读写锁(readers-writer lock),看英文可以顾名思义,在执行加锁操作时需要额外表明读写意图,复数读者之间并不互斥,而写者则要求与任何人互斥。

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

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

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

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

其中,

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

注意, 只要是涉及到 “互斥”, 就会产生线程的挂起等待. 一旦线程挂起, 再次被唤醒就不知道隔了多久了.
 
因此尽可能减少 “互斥” 的机会, 就是提高效率的重要途径

读写锁特别适合于 “频繁读, 不频繁写” 的场景中


🏁三. 重量级锁 vs 轻量级锁

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

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

重量级锁锁开销比较大,做的工作比较多。

  • 大量的内核态用户态切换
  • 很容易引发线程的调度

主要是依赖了 操作系统 提供的锁,使用这种锁,就容易产生阻塞等待。

轻量级锁锁开销比较小,做的工作比较少。

  • 少量的内核态用户态切换.
  • 不太容易引发线程调度

主要尽量避免使用 操作系统 提供的锁,而是尽量在用户态来完成功能,尽量避免 用户态 和 内核态 的切换,尽量避免挂起等待。

synchronized 是自适应锁,也是一个轻量级锁. 如果锁冲突比较严重, 就会变成重量级锁。


🚩四. 自旋锁 vs 挂起等待锁

  • 自旋锁:是轻量级锁的具体实现
  • 挂起等待锁:是重量级锁的具体实现

自旋锁是轻量级锁,也是乐观锁
挂起等待锁是重量级锁,也是悲观锁

按之前的方式,线程在抢锁失败后进入阻塞状态,放弃 CPU,需要过很久才能再次被调度。但实际上, 大部分情况下,虽然当前抢锁失败,但过不了很久,锁就会被释放。没必要就放弃 CPU. 这个时候就可以使用自旋锁来处理这样的问题。自旋锁发现锁冲突的时候,不会挂起等待,会迅速再来尝试看这个锁能不能获取到

自旋锁伪代码:

while (抢锁(lock) == 失败) {}

自旋锁特点:

  1. 一旦锁被释放,就可以第一时间获取到
  2. 如果锁一直不释放,就会消耗大量的 CPU

挂起等待锁特点:

  1. 一旦锁被释放,不能第一时间获取到
  2. 在锁被其他线程占用的时候,会放弃 CPU 资源

synchronized 作为轻量级锁的时候,内部是自旋锁,作为重量级锁的时候,内部是挂起等待锁。


🏳️‍🌈五. 公平锁 vs 非公平锁

啥样的情况才算公平?
 
认为符合 “先来后到” 这样的规则,就是公平

公平锁:遵守 “先来后到”. B 比 C 先来的. 当 A 释放锁的之后, B 就能先于 C 获取到锁.

非公平锁:不遵守 “先来后到”. B 和 C 都有可能获取到锁.

注意:

  • 操作系统内部对于挂起等待锁,就是非公平的(没有考虑先来后到),如果想要使用公平锁就要搞额外的数据结构来进行控制实现
  • 公平锁和非公平锁没有好坏之分, 关键还是看适用场景

synchronized 是非公平锁


🏴‍☠️六. 可重入锁 vs 不可重入锁

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

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

理解 “把自己锁死”

private static void func(){
   //第一次加锁
    synchronized (Demo26.class){
        //第二次加锁
        synchronized (Demo26.class){
             //...
        }
    }
}

按照之前对于锁的设定,第二次加锁的时候,就会阻塞等待,要等到第一次锁释放,这里的第二次加锁才能成功,但是第一次加锁释放不了,得第二次加锁成功代码继续往下走,才能走到第一次加锁的释放代码。

这就是个 “死锁!”。第二个锁加锁成功,依赖于第一个锁释放;第一个锁释放又依赖第二个锁加锁成功。

为了避免上述情况,就引入了 “可重入锁”,一个线程,可以对同一个锁,反复加锁多次,也没事!

可重入锁,在内部记录了这个锁是哪个线程获取到的,如果发现当前加锁的线程和持有锁的线程是同一个,则不挂起等待,而是直接获取到锁。同时还会给锁内部加上个计数器,记录当前是第几次加锁了,通过计数器来控制啥时候释放锁。

synchronized 也是可重入锁

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

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

相关文章

【数据分析之道-NumPy(七)】numpy字符串函数

文章目录 专栏导读1、函数说明2、add()函数3、multiply()函数4、center()函数5、capitalize()函数6、title()函数7、lower()函数8、upper()函数9、split()函数10、splitlines()函数11、strip()函数12、join()函数 专栏导读 ✍ 作者简介:i阿极,CSDN Pytho…

2023-04-16 学习记录--C/C++-邂逅C/C++

一、固定格式 ⭐️ stdio的理解: abbr.标准输入输出&#xff08;standard input/output&#xff09;。 #include <stdio.h> int main() {... // 表达式return 0; }二、printf语句&#xff08;输出语句&#xff09; ⭐️ &#xff08;一&#xff09;、前提【重要】 注意&a…

小程序组件化开发

前言 随着小程序的普及&#xff0c;越来越多的开发者开始使用小程序进行开发&#xff0c;而小程序的组件化开发已经成为了一种标配的开发模式。本文将深入介绍小程序组件化开发的相关知识&#xff0c;包括组件的定义、生命周期、通信和使用等方面&#xff0c;帮助大家更好地理解…

小程序WebSocket详解

1&#xff0c;什么是WebSocket&#xff1f; WebSocket是一种用于在Web浏览器和服务器之间进行双向通信的协议&#xff0c;而小程序WebSocket是在小程序中使用WebSocket协议进行双向数据通信的一种技术。它可以在单个TCP连接上进行全双工通信&#xff0c;实现实时、高效的数据通…

cesium加载geoserver发布的mvt服务

cesium 本身并不支持矢量切片的加载&#xff0c;所以需要借助其他工具进行解析。在Canvas中把矢量瓦片绘制好了&#xff0c;以图片形式像WMTS一样向Cesium提供图片服务就行了&#xff0c;当然也可以采用中间件的形式在后台把服务渲染好再以服务形式提供给前端。 方法1&#xf…

第八回:如何使用Stack Widget

文章目录 概念介绍使用方法示例代码 我们在上一章回中介绍了GirdView Widget,本章回中将介绍 Stack这种Widget&#xff0c;闲话休提&#xff0c;让我们一起Talk Flutter吧。 概念介绍 在Flutter中Stack主要用来叠加显示其它的Widget&#xff0c;类似我们日常生活中的楼层或者…

【Unity3D】魔方

1 需求实现 绘制魔方 中基于OpenGL ES 实现了魔方的绘制&#xff0c;实现较复杂&#xff0c;本文基于 Unity3D 实现了 2 ~ 10 阶魔方的整体旋转和局部旋转&#xff0c;详细需求如下&#xff1a; 用户通过选择魔方阶数&#xff0c;渲染指定阶数的魔方&#xff0c;并且可以自动打…

强化学习原理及应用作业之动态规划算法【SYSU_2023SpringRL】

强化学习原理及应用作业之动态规划算法【SYSU_2023SpringRL】 题目描述:任务一:动态规划方法一、策略迭代算法1、代码2、结果3、思路讲解策略评估策略提升二、价值迭代算法1、代码2、结果3、思路讲解算法整个流程总结题目描述: 本次实践作业将在以下环境进行: 该环境由一…

doris学习之路(一)初识Doris

Apache Doris 是一个基于 MPP 架构的高性能、实时的分析型数据库&#xff0c;以极速易用的特点被人们所熟知&#xff0c;仅需亚秒级响应时间即可返回海量数据下的查询结果&#xff0c;不仅可以支持高并发的点查询场景&#xff0c;也能支持高吞吐的复杂分析场景。基于此&#xf…

win10+Ubuntu双系统安装教程

1、下载Ubuntu镜像文件 Ubuntu各个版本的安装镜像文件可以从华为云下载&#xff1a; http://repo.huaweicloud.com/ubuntu-releases/ Ubuntu桌面版的安装镜像文件名为&#xff1a;ubuntu-<版本号>-desktop-amd64.iso 2、下载安装镜像工具 用win32diskImage 网址为Win…

初始c++语法

我们在C语言的基础之上进行c语言的学习。对于我们的c语言来说&#xff0c;c兼容C语言&#xff0c;所以我们以前编写的C语言的程序在c平台上也是可以运行的。唯一不同的就是我们的c对于我们C语言的部分语法做出了优化以及引入了面向对象的概念。所以在刚开始学习c的时候我们可以…

真题详解(归并)-软件设计(五十三)

真题详解&#xff08;UML部署图&#xff09;-软件设计&#xff08;五十二)https://blog.csdn.net/ke1ying/article/details/130233656 语句覆盖<判定覆盖<条件覆盖<路径覆盖。 2、ISO/IEC 9126软件质量模型中对软件质量定义&#xff1a; 易测试性、易分析性、稳定性、…

【FPGA-Spirit_V2】基于FPGA的循迹小车-小精灵V2开发板

&#x1f389;欢迎来到FPGA专栏~基于FPGA的循迹小车 ☆* o(≧▽≦)o *☆嗨~我是小夏与酒&#x1f379; ✨博客主页&#xff1a;小夏与酒的博客 &#x1f388;该系列文章专栏&#xff1a;FPGA学习之旅 文章作者技术和水平有限&#xff0c;如果文中出现错误&#xff0c;希望大家能…

Contrastive Representation Learning 对比表征学习(二)关键成分

原文翻译自这里 关键成分 大量数据增强 给定一训练样本&#xff0c;在计算其相关损失之前通常需要数据增强技术为该样本添加噪声。适度的数据增强设定对于学习一个优秀的&#xff0c;泛化性好的嵌入特征是至关重要的。其在不改变语义信息的同时将些微变量引入到样本之中&…

Windows环境下实现设计模式——备忘录模式(JAVA版)

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天总结一下Windows环境下如何编程实现备忘录模式&#xff08;设计模式&#xff09;。 不知道大家有没有这样的感觉&#xff0c;看了一大堆编程和设计模式的书&#xff0c;却还是很难理解设计模式&#xff…

【使用ChatGPT自动化】批量转换.csv文件为.xlsx文件

第1次提问&#xff1a; 我&#xff1a;我想使用Python批量转换.csv文件为.xlsx文件&#xff0c;请你提供代码 它&#xff1a;好的&#xff0c;以下是使用Python批量转换.csv文件为.xlsx文件的代码&#xff1a; import os import glob import pandas as pddef csv_to_xlsx(pa…

MATLAB数值运算(六)

目录 实验目的 实验内容 原创代码&#xff0c;仅供参考&#xff0c;不要直接CV呀 ~_~ 实验目的 1&#xff09;掌握定义符号对象和创建符号表达式的方法&#xff1b; 2&#xff09;掌握符号运算基本命令和规则&#xff1b; 3&#xff09;掌握符号表达式的运算法则以及符号矩阵…

A100 Jeston TX1TX2使用教程-接口说明

EdgeBox_Umate_A100是一款嵌入式人工智能计算机&#xff0c;能够为各类终端设备赋予人工智能的能力&#xff0c;降低终端智能的开发门槛。EdgeBox_Umate_A100搭载了移动处理器——NVIDIA Jetson TX1&#xff0c;并且兼容TX2&#xff0c;是最适合边缘计算的高性能平台。 A100 算…

【Javascript - 力扣每日一题】13. 罗马数字转整数

题目描述 罗马数字包含以下七种字符: I&#xff0c; V&#xff0c; X&#xff0c; L&#xff0c;C&#xff0c;D 和 M。 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如&#xff0c; 罗马数字 2 写做 II &#xff0c;即为两个并列的 1 。12 写做 XII &#xff0c;即为…

【黑马2023大数据实战教程】VMWare虚拟机部署HDFS集群详细过程

文章目录 部署HDFS集群1.配置workers:2.配置hadoop-env.sh文件3.配置core-site.xml文件4.配置hdfs-site.xml文件准备数据目录分发Hadoop文件夹配置环境变量授权为hadoop用户格式化文件系统错误排查方法!! 视频&#xff1a;黑马2023 VMWare虚拟机部署HDFS集群 注意&#xff01;这…