并发编程中的锁
创始人
2025-05-30 16:11:30

目录

  • 悲观锁与乐观锁
  • 公平锁与非公平锁
  • 独占锁与共享锁
  • 可重入锁与不可重入锁
  • 可中断锁与不可中断锁
  • 读/写锁
  • 自旋锁

悲观锁与乐观锁

悲观锁顾名思义就是持有悲观态度,线程每次进入临界区处理数据时,都认为数据很容易被其他线程修改

所以,在线程进入临界区前,会用户锁锁住临界区的资源,并在处理数据的过程中一直保持锁定状态。其他线程由于无法获取到对应的资源,就会阻塞等待,直到获取锁的线程释放锁,等待的线程才能获取到锁。
Java 中的 synchronized 重量级锁就是一种典型的悲观锁。

乐观锁顾名思义就是持有乐观的态度,认为每次访问数据的时候其他线程都不会修改数据。

所以,在访问数据的时候,不会对数据进行加锁操作。当涉及对数据更新的操作时,会检测数据是否被其他线程修改过:如果数据没有被修改过,则当前线程提交更新操作;如果数据被其他线程修改过,则当前线程会尝试再次读取数据,检测数据是否被其他线程修改过,如果再次检测的结果仍然为数据已经被其他线程修改过,则会再次尝试读取数据,如此反复,直到检测到的数据没有被其他线程修改过。

乐观锁在具体实现时,一般会采用版本号机制,先读取数据的版本号,在写数据时比较版本号是否一致,如果版本号一致则更新数据,否则再次读取版本号,比较版本号是否一致,直到版本号一致时更新数据。
Java 中的乐观锁一般都是基于 CAS 自旋实现的。在 Java 中,CAS 是一种原子操作,底层调用的是硬件层面的比较并交换的逻辑。在实现时,会比较当前值与传入的期望值是否相同,如果相同,则把当前值修改为目标值,否则不修改。

Java 中的 synchronized 轻量级锁属于乐观锁,是基于抽象队列同步器(AQS)实现的锁,如 ReentrantLock 等。

临界区:临界区一般表示能够被多个线程共享的资源或数据,但是每次只能提供给一个线程使用。临界区资源一旦被占用,其他线程就必须等待。
在并发编程中,临界区一般指受保护的对象或者程序代码片段,可以通过加锁的方式保证每次只有一个线程进入临界区,从而达到保护临界区的目的。

公平锁与非公平锁

公平锁的核心思想就是公平,能够保证各个线程按照顺序获取锁,也就是“先来先获取”的原则。

例如,存在三个线程,分别为线程 1 、线程 2 和线程 3 ,并依次获取锁。首先,线程 1 获取锁,线程 2 和线程 3 阻塞等待,线程 1 执行完任务释放锁。然后,线程 2 唤醒并获取锁,执行完任务释放锁。最后,线程 3 被唤醒并获取锁,执行完任务释放锁。这就是公平锁的流程。

非公平锁的核心思想就是每个线程获取锁的机会是不平等的,也是不公平的。先抢占锁的线程不一定能够先获取锁。

例如,存在三个线程,分别为线程 1 、线程 2 和线程 3 ,在线程 1 和线程 2 抢占锁的过程中,线程 1 获取到锁,线程 2 阻塞等待。线程 1 执行完任务释放锁后,在唤醒线程 2 时,线程 3 尝试抢占锁,则线程 3 是可以获取到锁的。这就是非公平锁。

在 Java 中,ReentrantLock 默认的实现为非公平锁,也可以在构造方法中传入 true 来创建公平锁对象。

独占锁与共享锁

独占锁也叫排他锁,在多个线程争抢锁的过程中,无论是读操作还是写操纵,只能有一个线程获取到锁,其他线程阻塞等待,独占锁采取的是悲观保守策略。

独占锁的缺点是无论对于读操作还是写操作,都只能有一个线程获取锁。但是读操作不会修改数据,如果当读操作线程获取锁时其他的读线程被阻塞,就会大大降低系统的读性能。此时,就需要用到共享锁了。

共享锁允许多个读线程同时获取临界区资源,它采取的是乐观锁的机制。共享锁会限制写操作与写操作之间的竞争,也会限制写操作与读操作之间的竞争,但是不会限制读操作与读操作之间的竞争。

在 Java 中,ReentrantLock 是一种独占锁,而 ReentrantReadWriteLock 可以实现读/写锁的分离,允许多个读操作同时获取读锁。

可重入锁与不可重入锁

可重入锁也叫递归锁,指同一个线程可以多次占用同一个锁,但是在解锁时,需要执行相同次数的解锁操作。

例如,线程 A 在执行任务的过程中获取锁,在后续执行任务的过程中,如果遇到抢占同一个锁的情况,则也会再次获取锁。
不可重入锁与可重入锁在逻辑上是相反的,指一个线程不能多次占用同一个锁。

例如,线程 A 在执行任务的过程中获取锁,在后续执行任务的过程中,如果遇到抢占同一个锁的情况,则不能再次获取锁。只有先释放锁,才能再次获取该锁。

在 Java 中,ReentrantLock 就是一种可重入锁。

可中断锁与不可中断锁

可中断锁与不可中断锁主要指线程在阻塞等待的过程中,能否中断自己阻塞等待的状态。

可中断锁指锁被其他线程获取后,某个线程在阻塞等待的过程中,可能由于等待的时间过长而中断阻塞等待的状态,去执行其他任务。

不可中断锁指锁被其他线程获取后,某个线程如果也想获取这个锁,就只能阻塞等待。如果占用锁的线程一致不释放锁,其他想获取锁的线程就会一直阻塞等待。

在 Java 中,ReentrantLock 是一种可中断锁,synchronized 则是一种不可中断锁。

读/写锁

读/写锁分别为读写和写锁,当持有读锁时,能够对共享资源进行读操作,当持有写锁的时,能够对共享资源进行写操作。写操作具有排他性,读锁具有共享性。在同一时刻,一个读/写锁只允许一个线程进行写操作,可以允许多个线程进行读操作。

当某个线程试图获取写锁时,如果发现其他线程已经获取到写锁或者读锁,则当前线程会阻塞等待,直到任何线程不再持有写锁或者读锁。

当某个线程试图获取读锁时,如果发现其他线程已经获取到读锁,则这个线程会直接获取到读锁。

当某个线程试图获取读锁时,如果发现其他线程已经获取到写锁,则这个线程会阻塞等待,直到占有写锁的线程释放锁。

在 Java 中,ReadWriteLock 是一种读/写锁。

自旋锁

自旋锁指某个线程在没有获取到锁时,不会立即进入阻塞等待状态,而是不断尝试获取锁,直到占有锁的线程释放锁。

自旋锁可能引起死锁和占用 CPU 时间过长的问题。

程序不能在占用自旋锁时调用自己,也不能在递归调用时获取相同的自旋锁,可以在一定程度上避免死锁。

当某个线程进入不断尝试获取锁的循环时,可以设定一个循环时间或循环次数,超过这个时间或者次数,就让线程进入阻塞等待的状态,在一定程度上可以有效的避免长时间占用 CPU 的问腿。

在 Java 中,CAS 是在一种自旋锁。

相关内容

热门资讯

Docker等容器技术如何与移... 移动应用程序的开发面临着很多挑战,包括开发环境的设置、测试的困难、部署的复杂性等。由于...
【微服务】—— Nacos安装... 文章目录1. Windows安装1.1 下载安装包1.2 解压1.3 端口配置1.4 启动1.5 访...
【OpenGL】 为了理解这个函数我们需要先学习一些OpenGL的内容 OpenGL可视化 https://g...
hjr-详细说一下Redis集... Redis作用 缓存 一般我们用Redis做缓存,热点数据 击穿:访问到...
【蓝桥杯】 C++ 数字三角形... 文章目录题目描述输入描述输出描述实现代码解题思路注意点知识点 题目描述 上图给出了一个数字三角形。从...
VR全景展会丨探索未来,重塑现... 随着科技的不断发展,虚拟现实(VR)技术逐渐成为一个重要的...
C++数据类型 目录 C++基础数据类型 指针 指针类型 指针赋值 引用 参考:《深...
超实用!!! 三分钟将你的项目... 文章目录前言一、在项目中新增配置二、配置github page setting?三、如...
数据结构---队列 专栏:数据结构 个人主页:HaiFan. 专栏简介:这里是...
数字操作方法 系列文章目录 前端系列文章——传送门 JavaScript系列文章——传送门 文章目录系列文章目录...
Cartesi 2023 年 ... 查看 Cartesi Machine、Cartesi Rollups 和 Noether 的更新正在...
JavaWeb——jsp概述入... JSP定义:  在如下一个jsp文件里面有如下的代码  <%@ page content...
一切喜怒哀乐都来自于你的认知 01 有个学子,准备出国,父母请来清华的教授宁向东。请问教授࿱...
JAVA并发编程——synch... 引言         Java语言为了解决并发编程中存在的原子性、可见性和有序性问题,...
git学习----3.21 未... 文章目录前言Git :一个分布式版本控制工具目标一、概述1.1 开发中的实际场景1.2...
Qt优秀开源项目之十七:QtP... QtPromise是Promises/A+规范的Qt/C++实现。该规范的译...
【前端八股文】JavaScri... 文章目录Set概念与arr的比较属性和方法并集、交集、差集Map概念属性和方法String用索引值和...
海康硬盘录像机接入RTSP/o... EasyNVR安防视频云服务平台可支持设备通过RTSP/Onvif协议接入平台,能提供...
在混合劳动力时代如何避免网络安... 在混合劳动力时代如何避免安全网络风险 三年多来,混合工作一直是工作生活中不可或缺的一...
2023还不懂Jmeter接口... 这里介绍的Jmeter接口测试的的实战,如果文章内容没遇看懂的话,我这边...
基于4G/5G弱网聚合的多链路... 基于4G/5G多卡聚合(弱网聚合)的智能融合通信设备技术亮点 增强带宽提供可靠连接 通过将多个有线和...
如何使用Synplify综合v... 文章目录使用Synplify综合的好处synplify的教程方法1(无效)...
2023年全国最新高校辅导员精... 百分百题库提供高校辅导员考试试题、辅导员考试预测题、高校辅导员考试真题、辅导员证考试题库等ÿ...
2022年18个值得期待的Le... 有数百个独特的LearnDash附加组件,您可能很难选择您的LearnDash LMS...
【java基础】Stream流... 文章目录基本介绍流的创建流的各种常见操作forEach方法filter方法map方法peek方法fl...
javaweb高校行政办公自动... 本课题基于我国高校管理信息化建设现状,结合在实际工作中所遇到的问题和收获,...
一款专门为自动化测试打造的集成... 你好,我是不二。 随着行业内卷越来越严重,自动化测试已成为测试工程师的...
【go-zero】golang... 一、casbin 概览 1、casbin基本了解 casbin的GitHub:https://git...
现在开发低代码平台算晚吗? 现在开发低代码平台算晚吗?作为低代码的亲戚——零代码厂商,这篇就以“厂商...
【JavaWeb】书城项目(2... 222.书城项目-第三阶段:修改所有html页面为jsp页面 改成jsp页面之后&#x...