锁策略详解:互斥锁、读写锁、乐观锁与悲观锁、轻量级锁与重量级锁、自旋锁、偏向锁、可重入锁与不可重入锁、公平锁与非公平锁

news2024/11/26 2:25:21


一.锁策略

锁策略指的是在多线程编程中用于管理共享资源访问的规则和技术。它们确保在任何给定时间只有一个线程可以访问共享资源,以防止竞态条件和数据不一致性问题。常见的锁策略包括:

  1. 互斥锁(Mutex):最常见的锁类型,用于确保同一时刻只有一个线程可以访问共享资源。其他线程必须等待当前线程释放锁。

  2. 读写锁(ReadWriteLock):允许多个线程同时读取共享资源,但只有一个线程可以写入共享资源。这有助于提高并发性能,因为读取操作不会阻塞其他读取操作。

  3. 悲观锁(Pessimistic Locking):假设会发生并发访问,因此在访问共享资源之前先获取锁。适用于写入操作频繁的场景。

  4. 乐观锁(Optimistic Locking):假设不会发生并发访问,因此在提交更改时检查是否有其他线程已经修改了资源。适用于读取操作频繁,且冲突不经常发生的场景。

  5. 自旋锁(Spin Lock):在获取锁时不会阻塞线程,而是使用循环等待直到锁可用。适用于短期占用情况,避免了线程切换的开销。

  6. ... ...

选择合适的锁策略需要根据具体的应用场景和性能要求来决定。不同的锁策略有不同的开销和适用场景,需要根据实际情况进行权衡和选择。

二.互斥锁

互斥锁(Mutex,Mutual Exclusion)是一种用于多线程编程的同步机制,用于确保在任何给定时间只有一个线程可以访问共享资源。互斥锁提供了两个主要操作:锁定(lock)和解锁(unlock)。在访问共享资源之前,线程需要先获取锁;在访问完成后,需要释放锁,以允许其他线程访问该资源。

互斥锁的特点包括:

  1. 互斥性:同一时间只有一个线程可以持有互斥锁,其他线程必须等待当前线程释放锁后才能获取锁。
  2. 阻塞:当一个线程尝试获取一个已经被其他线程持有的互斥锁时,该线程会被阻塞,直到获取到锁为止。
  3. 锁定状态:互斥锁可以有两种状态:锁定和未锁定。只有持有锁的线程才能访问共享资源。
  4. 安全性:互斥锁可以确保在多线程环境下对共享资源的安全访问,避免了竞态条件和数据不一致性问题。

在C/C++中,互斥锁通常由pthread_mutex_t表示,可以使用pthread_mutex_lock来获取锁,pthread_mutex_unlock来释放锁。在Java中,可以使用synchronized关键字来实现互斥锁的功能,也可以使用ReentrantLock类来实现互斥锁。

三.读写锁

读写锁(ReadWriteLock)是一种并发控制机制,允许多个线程同时读取共享资源,但在写入时需要互斥,即只能有一个线程进行写操作,且写操作时禁止读取操作。读写锁的主要目的是在读操作频繁、写操作较少的情况下提高并发性能。

读写锁有两种状态:读取锁和写入锁。在读取锁状态下,多个线程可以同时获取读取锁,从而允许并发读取操作。在写入锁状态下,只有一个线程可以获取写入锁,其他线程无法获取读取或写入锁,直到写入操作完成。

读写锁的特点包括:

  1. 读写分离:读取操作不会阻塞其他读取操作,但会阻塞写入操作。这样可以提高读取操作的并发性能。

  2. 写入互斥:写入操作会阻塞其他读取和写入操作,确保在写入操作时不会有其他线程对共享资源进行读取或写入。

  3. 公平性:读写锁可以是公平的,即按照请求锁的顺序分配锁;也可以是非公平的,即允许插队获取锁。

Java中的读写锁由ReentrantReadWriteLock实现,读写锁特别适合于 "频繁读, 不频繁写" 的场景中。

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

四.乐观锁与悲观锁

乐观锁(Optimistic Locking)和悲观锁(Pessimistic Locking)是两种不同的并发控制机制,用于处理多线程环境下的数据访问冲突。

  1. 乐观锁(Optimistic Locking)

    • 乐观锁的核心思想是假设在大多数情况下,数据并发访问冲突的概率很低,因此不需要加锁。在读取数据时,不会加锁,而是在尝试更新数据时才会进行检查。
    • 在使用乐观锁时,通常会在读取数据时记录下数据的版本号或者时间戳,在尝试更新数据时检查这个版本号或者时间戳是否发生变化。如果没有变化,则说明在读取到写入之间数据没有被其他线程修改,可以进行更新操作;如果发生变化,则表示数据被修改过,需要进行冲突处理,如回滚或者重试等。
    • 乐观锁适用于读操作频繁、写操作相对较少的场景,可以减少锁竞争,提高并发性能。
  2. 悲观锁(Pessimistic Locking)

    • 悲观锁的核心思想是假设在数据访问时会发生并发冲突,因此在访问数据之前先加锁,确保在任何时候只有一个线程可以访问数据,其他线程需要等待。
    • 悲观锁适用于写操作频繁的场景,可以有效避免数据被并发修改而导致的问题。常见的悲观锁实现包括互斥锁(Mutex)和读写锁(ReadWriteLock)等。

总的来说,乐观锁适合于并发写入比较少、并发读取比较多的场景,可以提高系统的并发性能;而悲观锁适合于并发写入比较频繁的场景,可以保证数据的一致性和完整性。选择合适的锁策略取决于具体的应用场景和需求。

举个例子来说

同学 A 认为 "老师是比较忙的, 我来问问题, 老师不一定有空解答"。因此同学 A 会先给老师发消息: "老师你忙嘛? 我下午两点能来找你问个问题嘛?" (相当于加锁操作) 得到肯定的答复之后,才会真的来问问题。 如果得到了否定的答复,那就等一段时间,下次再来和老师确定时间。这个是悲观锁。

同学 B 认为 "老师是比较闲的, 我来问问题, 老师大概率是有空解答的"。因此同学 B 直接就来找老师(没加锁, 直接访问资源) 如果老师确实比较闲,那么直接问题就解决了。如果老师这会确实很忙,那么同学 B 也不会打扰老师,就下次再来(虽然没加锁,但是能识别出数据访问冲突)。这个是乐观锁。

这两种思路不能说谁优谁劣,而是看当前的场景是否合适。如果当前老师确实比较忙,那么使用悲观锁的策略更合适,使用乐观锁会导致 "白跑很多趟", 耗费额外的资源。如果当前老师确实比较闲, 那么使用乐观锁的策略更合适,使用悲观锁会让效率比较低。

五.重量级锁与轻量级锁

轻量级锁和重量级锁是 Java 虚拟机中用于实现锁的两种不同策略,主要针对 synchronized 关键字所提供的锁的不同实现方式。

  1. 轻量级锁(Lightweight Lock)

    • 轻量级锁是为了在多线程竞争情况下,提高性能而引入的一种锁优化技术。当一个线程尝试获取锁时,如果锁没有被其他线程占用,虚拟机会在当前线程的栈帧中使用 CAS 操作尝试将对象头部的 Mark Word 替换为指向当前线程的锁记录指针(Lock Record Pointer)。
    • 如果 CAS 操作成功,当前线程就获得了锁,并且锁的状态被标记为轻量级锁。此时其他线程访问同步块时会尝试自旋等待,而不是直接阻塞,以减少线程切换的开销。
    • 如果自旋等待一段时间后仍无法获取锁,或者其他线程争用激烈,CAS 操作失败,那么轻量级锁会膨胀为重量级锁。
  2. 重量级锁(Heavyweight Lock)

    • 当轻量级锁膨胀失败时,锁会升级为重量级锁。重量级锁会使其他线程阻塞,而不是进行自旋等待,防止CPU空转浪费资源。
    • 重量级锁的实现通常是通过操作系统的互斥量(Mutex)来实现的,当一个线程获取了锁,其他线程就无法获取锁,只能在等待队列中等待锁释放。

总的来说,轻量级锁适用于线程竞争不激烈的情况,可以减少线程切换的开销,提高性能;而重量级锁适用于线程竞争激烈的情况,可以保证数据的一致性和完整性。 Java 虚拟机会根据锁竞争的情况自动选择合适的锁实现方式。

六.自旋锁

自旋锁是一种基于循环重试的锁机制,在多线程编程中用于实现对共享资源的互斥访问。当一个线程尝试获取自旋锁时,如果锁已被其他线程持有,该线程不会立即进入阻塞状态,而是在循环中不断尝试获取锁,直到成功获取为止,或者达到最大尝试次数后才会放弃。

自旋锁的主要特点包括:

  1. 循环等待:获取自旋锁的线程会在一个循环中不断尝试获取锁,直到成功或者达到一定的尝试次数。
  2. 忙等待:自旋锁的获取过程是忙等待的,即线程在等待锁的过程中会一直占用CPU资源。
  3. 无阻塞:自旋锁的获取过程不会使线程进入阻塞状态,适用于对锁的占用时间较短的情况。
  4. 性能:自旋锁适用于锁竞争不激烈、等待锁时间较短的情况,可以减少线程切换的开销,提高性能。
  5. 等待限时:为了避免自旋锁的线程一直占用CPU资源,通常会设置一个最大尝试次数或者超时时间,超过这个限制后线程会放弃自旋并进入阻塞状态等待锁的释放。

自旋锁在并发度高、锁竞争不激烈的情况下可以有效地提高性能,但在锁竞争激烈、持有锁时间较长的情况下,自旋锁可能会导致性能下降。因此,在选择锁机制时需要根据实际情况进行权衡和选择。在Java中,java.util.concurrent包提供了ReentrantLock的实现,其中包括了可重入锁和自旋锁的功能。

七.偏向锁

偏向锁是Java虚拟机为了减少无竞争情况下的同步原语的性能消耗而引入的一种优化手段。当一个线程访问同步块时,如果该同步块没有被锁定或者被其他线程锁定,那么这个线程会尝试获取偏向锁,并将对象头中的标记设置为指向自己的线程ID。之后,当这个线程再次进入同步块时,不需要再次获取锁,因为对象头中已经记录了这个线程已经获取了偏向锁。

偏向锁适用于大多数情况下都是单线程访问同步块的场景,因为在这种情况下,偏向锁可以显著减少获取锁的代价。但是,如果有其他线程竞争锁,那么偏向锁会失效,对象会膨胀为重量级锁,这样会增加获取锁的代价。

偏向锁的优点包括:

  1. 减少同步操作的耗时,提高程序性能;
  2. 对于无竞争的场景,减少不必要的线程切换。

需要注意的是,偏向锁只适用于无竞争的情况,如果存在竞争,会导致额外的性能损失。因此,在使用偏向锁时,需要根据实际场景进行评估和选择。

八.公平锁与非公平锁

公平锁和非公平锁是两种不同的锁获取策略,用于多线程环境下对共享资源的访问控制。

  1. 公平锁

    • 公平锁是指多个线程按照请求锁的顺序获取锁,即先到先得的原则。当一个线程尝试获取公平锁时,如果锁当前被其他线程持有,该线程会进入等待队列,按照先来后到的顺序等待锁的释放。
    • 公平锁保证了锁的获取是按照请求的顺序进行的,避免了某些线程长时间无法获取到锁的情况,确保了线程获取锁的公平性。
  2. 非公平锁

    • 非公平锁是指多个线程尝试获取锁时,并不考虑锁的等待队列中是否有等待线程,而是直接尝试获取锁。如果锁当前未被其他线程持有,则获取成功;如果锁已被其他线程持有,则当前线程可能会被阻塞,也可能会一直尝试获取锁。
    • 非公平锁相比于公平锁,可能会导致某些线程长时间无法获取到锁,存在一定的不公平性。但由于不需要维护等待队列的顺序,非公平锁的性能通常会比公平锁更好。

在Java中,ReentrantLockReentrantReadWriteLock可以指定为公平锁或非公平锁。在创建锁时,可以通过构造方法来指定锁的公平性。公平锁在一定程度上保证了线程获取锁的顺序,适用于对锁的公平性有要求的场景;而非公平锁则更适用于对性能要求较高,可以容忍一定程度的不公平性的场景。

九.可重入锁与不可重入锁

可重入锁(Reentrant Lock)和不可重入锁是两种锁的概念,主要区别在于同一个线程能否多次获取同一把锁。

  1. 可重入锁

    • 可重入锁是指同一个线程可以多次获取同一把锁,而不会发生死锁。线程每次获取锁时,锁的计数器会加一;每次释放锁时,计数器会减一。只有当计数器为零时,锁才会被完全释放。
    • 可重入锁的一个典型应用场景是方法递归调用。当一个同步方法内部调用同一个类的其他同步方法时,如果没有可重入锁,就会发生死锁。而可重入锁能够保证线程在递归调用时不会被自己阻塞。
  2. 不可重入锁

    • 不可重入锁是指同一个线程不能多次获取同一把锁。如果一个线程已经持有了某个锁,再次尝试获取该锁时会被阻塞,直到锁被释放。
    • 不可重入锁在一些情况下可能会导致死锁,因为线程在持有锁的情况下又试图获取同一把锁,会造成自己阻塞而无法释放已持有的锁。

Java中的synchronized关键字实现的锁是可重入锁,即同一个线程可以重复获取同一把锁。而ReentrantLock也是可重入锁的一种实现。可重入锁能够简化并发编程中对锁的使用,避免了一些潜在的死锁情况,提高了程序的可靠性和健壮性。




 本次的分享就到此为止了,希望我的分享能给您带来帮助,创作不易也欢迎大家三连支持,你们的点赞就是博主更新最大的动力!如有不同意见,欢迎评论区积极讨论交流,让我们一起学习进步!有相关问题也可以私信博主,评论区和私信都会认真查看的,我们下次再见

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

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

相关文章

WPS表格:对比少于1万的两列数据

当我们需要对于A、B两列乱序的数据,找出A列中某一项B列有没有,或者找出B列中的某一项A列有没有,都可以先将这两列数据放入WPS表格中: 1.选中C列的第一行的单元格,在函数区输入函数 如果我们以A为基准,找A中…

项目分享|基于ELF 1S开发板完成的物联网开源项目

ElfBoard作为飞凌嵌入式旗下教育品牌,自成立以来,持续吸引着各界的瞩目,其中也赢得了一些工程师的青睐。今天,就和各位小伙伴分享一位杰出工程师借助ELF 1S开发板完成的嵌入式物联网项目,见证智慧与技术的火花。 关于…

二、计算机基础(Java零基础二)

🌻🌻目录 一、认识计算机二、计算机的组成2.1 计算机硬件(摸得着,看得见)2.1.1 计算机硬件组成2.1.2 冯.诺依曼(计算机之父)体系结构 2.2 计算机软件(摸不着,看不见) 三、电脑常用快…

LVDS 源同步接口

传统数据传输通常采用系统同步传输方式,多个器件基于同一时钟源进行系统同步,器件之间的数据传输时序关系以系统时钟为参考,如图1所示。系统同步传输方式使各器件处于同步工作模式,但器件之间传输数据的传输时延难以确定&#xff…

Remix 集成 MUI

Remix 如何接入 MUI 组件库,MUI 官网提供了一个 Remix 接入 MUI 的例子,用的是老的 Remix版本,如何接入新的 Vite 版本呢? 由于 MUI 支持 SSR,只需要改造对应的 Client 和 Server 即可实现。安装 MUI 组件组件库&…

实现字符串复制(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int i 0;char a[100], b[100];//获取字符串&#xff1b;printf("请为数组a输入字符串…

如何在外网访问内网共享文件?

在日常工作和生活中&#xff0c;我们经常会遇到外网访问内网共享文件的需求。我们可能需要远程访问公司内部的共享文件夹&#xff0c;或者与不同地区的合作伙伴共享文件。由于网络安全的限制&#xff0c;外网访问内网的共享文件并不是一件容易的事情。 为了解决这个问题&#x…

java项目之车辆管理系统(springboot+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的车辆管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 车辆管理系统的主要使用者分…

Python 旋转立方体

文章目录 效果图运行环境完整代码实现思路1. 导入库和定义常量2. 创建Cube类3. 实现Cube类的draw方法4. 实现主函数 效果图 运行环境 python版本&#xff1a;python3.x 依赖包&#xff1a; $ pip install pygame $ pip install numpy完整代码 import numpy as np # 导入 N…

【YOLOv8模型网络结构图理解】

YOLOv8模型网络结构图理解 1 YOLOv8的yaml配置文件2 YOLOv8网络结构2.1 Conv2.2 C3与C2f2.3 SPPF2.4 Upsample2.5 Detect层 1 YOLOv8的yaml配置文件 YOLOv8的配置文件定义了模型的关键参数和结构&#xff0c;包括类别数、模型尺寸、骨干&#xff08;backbone&#xff09;和头部…

单调栈问题

原理 单调栈的核心原理是&#xff1a;在栈内保持元素的单调性&#xff08;递增或递减&#xff09; 单调递增栈&#xff1a; 用于处理“下一个更小的元素”问题。当新元素比栈顶元素小或等于时&#xff0c;直接入栈&#xff1b;否则&#xff0c;一直从栈顶弹出元素&#xff0c…

会声会影2024中文汉化补丁器附免费激活码序列号

会声会影是一款由加拿大Corel公司发布的视频编辑软件&#xff0c;它以其功能丰富和用户友好的界面而闻名。会声会影2024是该系列的最新版本&#xff0c;它不仅继承了之前版本的强大功能&#xff0c;还引入了一系列新的特性和工具&#xff0c;使得视频编辑更加简单、高效且富有创…

【简单讲解下Fine-tuning BERT】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

【图解计算机网络】TCP 重传、滑动窗口、流量控制、拥塞控制

TCP 重传、滑动窗口、流量控制、拥塞控制 TCP 重传超时重传快速重传 滑动窗口流量控制拥塞控制慢启动拥塞避免拥塞发生快速恢复 TCP 重传 TCP重传是当发送的报文发生丢失的时候&#xff0c;重新发送丢失报文的一种机制&#xff0c;它是保证TCP协议可靠性的一种机制。 TCP重传…

【Oracle篇】rman物理备份工具的基础理论概述(第一篇,总共八篇)

☘️博主介绍☘️&#xff1a; ✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ ✌✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux&#xff0c;也在扩展大数据方向的知识面✌✌️ ❣️❣️❣️大佬们都喜欢静静的看文章&am…

试衣不再有界:Tunnel Try-on开启视频试衣应用新纪元

论文&#xff1a;https://arxiv.org/pdf/2404.17571 主页&#xff1a;https://mengtingchen.github.io/tunnel-try-on-page/ 一、摘要总结 随着虚拟试衣技术的发展&#xff0c;消费者和时尚行业对于能够在视频中实现高质量虚拟试衣的需求日益增长。这项技术允许用户在不实际穿…

云计算十三课

centos安装 点击左上角文件 点击新建虚拟机 点击下一步 点击稍后安装操作系统&#xff0c;下一步 选择Linux&#xff08;l&#xff09;下一步 设置虚拟机名称 点击浏览选择安装位置 新建文件夹设置名称不能为中文&#xff0c;点击确定 点击下一步 设置磁盘大小点击下一步…

修改MTU值解决Linux下运行top命令卡死问题

上周明月的Linux服务器上运行top命令总是莫名的出现卡死现象&#xff0c;甚至是CtrlC都无法终止进程&#xff0c;今天终于抽空找到了解决办法&#xff0c;原来是需要修改Linux的MTU值&#xff0c;将服务器操作系统数据包调小&#xff0c;加上VxLAN数据包小于1500即可。 top命令…

HCIP【BGP综合实验】

目录 一、实验拓扑图&#xff1a; 二、实验要求&#xff1a; 三、实验思路&#xff1a; 四、实验步骤&#xff1a; 1、进行网段的子网划分&#xff08;整个实验总共有19条网段&#xff09;&#xff1a; (1)首先&#xff0c;根据实验要求&#xff0c;将172.16.0.0/16全部划…

英伟达发布AM-RADIO高效视觉基础模型,推理速度提升6倍,性能超CLIP、DINOv2、SAM

前言 近年来&#xff0c;视觉基础模型 (VFM) 在众多下游任务中取得了巨大成功&#xff0c;例如图像分类、目标检测和图像生成等。然而&#xff0c;现有的 VFM 通常专注于特定领域&#xff0c;例如 CLIP 擅长零样本视觉语言理解&#xff0c;DINOv2 擅长语义分割&#xff0c;SAM…