并发编程中常见的锁策略

目录

一. 悲观锁和乐观锁

二. 重量级锁和轻量级锁

三. 挂起等待锁和自旋锁

四. 公平锁和非公平锁

五. 可重入锁和不可重入锁

六. 读写锁

七. synchronized

八. 锁消除

九. 锁粗化


一. 悲观锁和乐观锁

1. 乐观锁: 乐观锁 在加锁时, 假设出现锁冲突的概率不大 --> 接下来围绕加锁做的工作就更少.

2. 悲观锁: 悲观锁 在加锁时, 假设出现锁冲突的概率很大 --> 接下来围绕加锁做的工作就更多.

[注]: synchronized这个锁是"自适应锁". 它在初始情况下是乐观的, 预估出现锁冲突的概率不大. 但是会统计锁冲突的次数, 当所冲突的次数达到一定值之后, 就会从乐观锁转变为悲观锁.

二. 重量级锁和轻量级锁

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

        (一般来说, 悲观锁都是重量级的).

2. 轻量级锁: 加锁的开销比较小, 围绕加锁要做的工作更少.

        (一般来说, 乐观锁都是轻量级的).

三. 挂起等待锁和自旋锁

1. 挂起等待锁: 挂起等待锁 就是悲观锁(重量级锁)的一种典型实现.

挂起等待锁采用的等待策略是 "挂起等待", 等待过程中释放CPU资源, 这样的话就不用一直占用CPU等待锁资源, 可以让出CPU资源做别的事情了. 如果锁资源释放, 会以某种方式通知该线程.

2. 自旋锁: 自旋锁 就是乐观锁(轻量级锁)的一种典型实现.

自旋锁采用的等待策略是 "忙等", 等待过程中不会释放CPU资源, 等待过程中一直不停地检测锁是否被释放, 如果释放, 就有机会立即获取锁资源.

四. 公平锁和非公平锁

1. 公平锁: 公平锁确保 锁的获取是按照线程请求的顺序进行的. (即: 最先请求的线程会最先获取到锁).

2. 非公平锁: 非公平锁不能保证 锁的获取是按照线程锁请求的顺序进行的. (当锁被释放时, 任何线程都有机会获得锁, 即使是刚开始尝试获取锁的线程).

[注]: synchronized就属于非公平锁, 当synchronized的锁资源释放后, 等待锁资源的n个线程就会重新竞争, 下一个是哪个线程拿到锁是不确定的.

五. 可重入锁和不可重入锁

1. 可重入锁: 可重入锁是指同一个线程可以多次获取同一把锁. 如果某线程已持有锁, 那么当该线程再次尝试获取该锁资源时 (再次进入由这个锁保护的代码块) , 不会发生阻塞.

(可重入锁可以防止死锁的发生,因为同一个线程可以重复获取已经持有的锁)

2. 不可重入锁: 不可重入锁是指如果某线程已经持有锁, 那么它不能再次获取这个锁, 否则就会导致死锁.

java中也对可重入锁进行了封装. java中用 ReentrantLock 这个类来实现可重入锁.

3. ReentrantLock 和 synchronized的区别

(1) synchronized 是关键字, 而ReentrantLock 是java标准库中的一个类.

(2) synchronized 通过代码块加锁解锁, 而ReentrantLock通过 lock() 和 unlock() 实现加锁解锁.

(3) synchronized 没有 try-lock 这样的锁风格, 当加锁失败的时候, 就会阻塞等待, 等待锁资源释放;  而ReentrantLock提供了 try-lock 这样的锁风格, 当加锁失败的时候, 不会阻塞等待, 而是直接返回一个返回值, 通过返回值来表示加锁成功还是失败.

 (3) synchronized 一定是非公平锁, 无法修改;  而 ReentrantLock 默认为非公平锁, 但是可以通过给构造方法传入参数来把 ReentrantLock 设定成公平锁.

(4) synchronized 的等待和唤醒都是通过wait() -- notify() 来完成的;  而ReentrantLock提供了功能更强的"等待--通知"机制, 基于Condition类实现. 

六. 读写锁

读写锁是一种用于解决多线程环境中读操作和写操作之间冲突的锁机制. 读写锁可以提高并发程序的性能, 尤其是在读操作远多于写操作的场景中.

1. 分类:

(1) 读锁: 多个线程可以同时持有读锁.

(2) 写锁: 写锁是"排他"的, 即任何时候只能有一个线程持有写锁, 并且在此期间不能有其他线程进行读操作或者写操作.

2. 特点:

(1) 读读共享: 多个线程可以同时获取读锁.

(2) 读写互斥: 读操作和写操作不能同时进行. (如果某线程持有写锁, 那么其他线程无法获取读锁或者写锁)

(3) 写写互斥: 写操作不能同时进行. (如果某线程持有写锁, 那么其他线程不能获取写锁)

3. 实现方式

java中,  ReadWriteLock 接口 它的实现类 ReentrantReadWriteLock 提供了读写锁的功能.

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Demo33 {public class ReadWriteLockExample {private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); //创建一个读写锁对象private int data = 0; // 共享数据// 读操作public int read() {readWriteLock.readLock().lock(); //调用读锁try {// 执行读操作return data;} finally {readWriteLock.readLock().unlock();}}// 写操作public void write(int value) {readWriteLock.writeLock().lock(); //调用写锁try {// 执行写操作data = value;} finally {readWriteLock.writeLock().unlock();}}}}

上述代码中, read操作获取读锁, 允许多个线程同时获取数据; write操作获取写锁, 确保在写入或修改数据时没有其他线程进行读写操作.

七. synchronized

1. synchronized锁的特点

(1) 悲观乐观 --> 自适应

(2) 重量轻量 --> 自适应

(3) 挂起等待 / 自旋 --> 自适应

(4) 是非公平锁

(5) 是可重入锁

(6) 不是读写锁

2. synchronized的加锁过程

synchronized的加锁过程, 实际上是一个"锁升级"的过程.

使用synchronized加锁, 刚开始, synchronized会处于"偏向锁"的状态, 即只是 "做个标记" , 但不会真正加锁. 然后, 如果出现锁竞争的话,  "偏向锁" 会升级到 "轻量级锁",  之后, 线程进一步统计锁竞争的次数和频率, 当达到一定程度的时候, "轻量级锁" 就会升级到 "重量级锁".

(synchronized加锁过程:  无锁 --> 偏向锁 --> 轻量级锁 --> 重量级锁)

[注]: 偏向锁, 并不会真的加锁, 而只是"做一个标记", 标记的过程, 开销很小, 非常的轻量和高效.

八. 锁消除

"锁消除"机制 是编译器的自动优化策略. 比如我们写了一个带synchronized锁的代码, 编译器就会对我们加锁的代码做出判定, 如果判断得到这里没有必要加锁, 就会自动把这里的锁消除掉.

九. 锁粗化

在说锁粗化之前, 我们首先得明确一个概念: "锁的粒度".  -->  一个锁保护的代码范围越广, 那么这个锁的粒度就越粗;  一个锁保护的代码范围越小, 那么这个锁的粒度就越细.

那么锁的粗化, 就是把多个"细粒度"的锁, 合成粗粒度的锁. 当编译器检测到一系列连续的操作都在对同一个锁进行加锁和解锁时, 会将这些操作合并为一个更大的锁区域, 从而减少锁的开销.

锁粗化的优点: 减少了对锁的获取和释放次数, 降低了上下文切换和调度的开销, 减少了线程因频繁获取和释放锁而产生的竞争, 提高了程序的吞吐量。

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

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

相关文章

ENSP作业——小型园区网

题目 根据上图,可得需求为: 1.配置交换机上的VLAN及IP地址。 2.设置SW1为VLAN 2/3的主根桥,设置SW2为VLAN 20/30的主根桥,且两台交换机互为主备。 3.可以使用super vlan。(本次实验中未使用) 4.上层通过静…

计算机网络:运输层 —— 运输层端口号

文章目录 运输层端口号的分类端口号与应用程序的关联应用举例发送方的复用和接收方的分用 运输层端口号的分类 端口号只具有本地意义,即端口号只是为了标识本计算机网络协议栈应用层中的各应用进程。在因特网中不同计算机中的相同端口号是没有关系的,即…

【C++练习】使用C++编写程序计算π的近似值

题目:使用C编写程序计算π的近似值 描述: 编写一个C程序,使用一个特定的数学公式来计算圆周率(π)的近似值。该程序定义了一个函数calculatePi(),该函数通过一个迭代算法和一个涉及反正切函数(…

Hook小程序

下载: https://github.com/JaveleyQAQ/WeChatOpenDevTools-Python 配置: pip install -r requirements 实现: 开启小程序开发者模式,类似浏览器F12 效果: 使用: 退出微信,进入安装的目录…

如何在pycharm中 判断是否成功安装pytorch环境

1、在电脑开始端,找到 2、打开后 在base环境下 输入conda env list 目前我的环境中没有pytorch 学习视频:【Anaconda、Pytorch、Pycharm到底是什么关系?什么是环境?什么是包?】https://www.bilibili.com/video/BV1CN411s7Ue?vd_sourcefad0750b8c6…

AI陪伴走热,网易云信“融合通讯+AI”新方案发布!附场景App及源码

信息秒回、不会失联、724h 情感陪伴、随时提供情绪价值......在 AI 能力越来越强大的今天,我们开始有了“AI 助手”、“AI 搭子”,甚至开始谈起“AI 男友/女友”,AI 的角色早已超越了简单的生产力工具,它正深入到我们生活的方方面…

力扣 LeetCode 704. 二分查找(Day1:数组)

解题思路: 二分查找主要分为[ left , right ]左闭右闭和[ left , right )左闭右开两种 此处采取[ left , right ]左闭右闭写法 注意: 1. right的初始化取值 2. while中取等 3. right mid -1 ; class Solution {public int search(int[] nums, i…

java-AOP编程示例

SpringBoot工程,有不懂的留言or Kimi一下 LogAspect.java package com.xxx.javaaopdemo.Aspect;import com.xxx.javaaopdemo.LogAnnotation; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang…

Kafka入门:Java客户端库的使用

在现代的分布式系统中,消息队列扮演着至关重要的角色,而Apache Kafka以其高吞吐量、可扩展性和容错性而广受欢迎。本文将带你了解如何使用Kafka的Java客户端库来实现生产者(Producer)和消费者(Consumer)的基…

使用 npm 安装 Yarn

PS E:\WeChat Files\wxid_fipwhzebc1yh22\FileStorage\File\2024-11\spid-admin\spid-admin> yarn install yarn : 无法将“yarn”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后…

力扣617:合并二叉树

给你两棵二叉树: root1 和 root2 。 想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠&#…

谷歌浏览器支持的开发者工具详解

谷歌浏览器(Google Chrome)是全球最受欢迎的网页浏览器之一,它不仅提供了快速、安全的浏览体验,还为开发者提供了强大的开发者工具。本文将详细介绍如何使用谷歌浏览器的开发者工具,并解答一些常见问题。(本…

HTB:OpenAdmin[WriteUP]

目录 连接至HTB服务器并启动靶机 使用nmap对靶机TCP端口进行开放扫描 继续使用nmap对靶机22、80端口进行脚本、服务扫描 使用Dirbuster对靶机网页路径进行递归扫描 ​编辑 尝试在searchsploit中搜索改WebAPP漏洞 横向移动(其实没有横) 启动Metasploit 特权提升 USER_…

IO作业5

设置双信号实现交替生产者线程和消费者线程 #include <myhead.h> int n0; pthread_mutex_t fastmutex;//定义互斥锁 pthread_cond_t cond;//定义条件变量 pthread_cond_t cond2; void *product() {int i;for(i0;i<10;i){n;printf("我生产了一台特斯拉n%d\n"…

Web3.0安全开发实践|BNB链安全开发,这10大实用tips一定要收藏

BNB Chain是Web3世界中最受欢迎的区块链之一&#xff0c;其费用合理、交易迅速以及项目生态系统丰富几大原因吸引了广大用户。与任何的区块链都一样&#xff0c;BNB Chain上的开发者在开发过程中首先考虑的应该是安全问题&#xff1a;因为任何资金的损失都会导致用户对协议及平…

QT开发笔记之小知识

QCoreApplication::aboutToQuit 主事件循环退出前发出的信号&#xff0c;是程序退出前等待QT线程退出回收资源的神器。 官方帮助文档 [signal] void QCoreApplication::aboutToQuit() 该信号在应用程序即将退出主事件循环时发出&#xff0c;例如&#xff1a;当事件循环级别降至…

插入排序(C语言)

直接插入排序的基本思想&#xff1a;把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的记录插入完为止&#xff0c;得到一个新的有序序列 。 一、步骤 1.给定一个乱序的数组&#xff0c;如 从第一个元素开始排序&#xff0c;当只…

文心一言 VS 讯飞星火 VS chatgpt (389)-- 算法导论25.1 2题

二、为什么要求对于所有的 1 ⩽ i ⩽ n 1⩽i⩽n 1⩽i⩽n&#xff0c;有 w i i 0 w_{ii}0 wii​0 &#xff1f;如果要写代码&#xff0c;请用go语言。 文心一言&#xff1a; 在许多数学和计算应用中&#xff0c;要求矩阵 W W W 的对角线元素 w i i 0 w_{ii} 0 wii​0 是…

Java多线程详解⑦(全程干货!!!)内存可见性 || volatile || JMM || wait notify notifyAll

这里是Themberfue 在上一节中&#xff0c;我们讨论了死锁的概念&#xff0c;产生的场景 &#xff0c;产生的必要条件...... 内存可见性 我们先来看一段百度百科关于 "内存可见性" 的解释 "内存可见性" 就是造成线程安全问题的原因之一 如果是单纯地看…

安装双系统(linux操作系统(debian)安装)

参考博客&#xff1a;戴尔服务器安装Debian11过程_戴尔t130安装debian-CSDN博客 一.腾出一个50G以上的空间&#xff0c;准备装操作系统 1.底部搜索计算机管理&#xff0c;选择磁盘管理 本人已预留400GB磁盘空间安装ubuntu系统&#xff0c;若没有预留空间&#xff0c;则可以选…