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

常见锁类型介绍

下面结合代码详细介绍 MutexRW LockFutex自旋锁信号量条件变量 和 synchronized,并分析它们的适用场景、特点以及为什么这些锁适用于特定场景。我们将从锁的实现机制和性能特点出发,解释其适用性。


1. Mutex(互斥锁)

代码示例

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx; // 定义一个互斥锁
int shared_data = 0;void increment() {for (int i = 0; i < 100000; ++i) {mtx.lock();   // 加锁++shared_data; // 访问共享资源mtx.unlock(); // 解锁}
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "Final value of shared_data: " << shared_data << std::endl;return 0;
}

特点

  • 独占访问:同一时间只有一个线程可以持有锁。

  • 阻塞等待:如果锁被占用,请求锁的线程会被阻塞,直到锁被释放。

  • 不可重入:标准 mutex 不可重入,但可以通过递归 mutex 实现重入。

适用场景

  • 保护临界区:确保同一时间只有一个线程访问共享资源。

  • 锁持有时间较长:适用于锁持有时间较长的场景,因为阻塞等待不会浪费 CPU 资源。

为什么适用

  • 实现机制:Mutex 通过操作系统提供的阻塞机制(如 futex)实现,线程在锁被占用时会进入睡眠状态,不会占用 CPU。

  • 性能特点:适合锁持有时间较长的场景,因为线程睡眠不会浪费 CPU 资源。


2. RW Lock(读写锁)

代码示例

#include <iostream>
#include <thread>
#include <shared_mutex>std::shared_mutex rw_mtx; // 定义一个读写锁
int shared_data = 0;void reader(int id) {for (int i = 0; i < 5; ++i) {rw_mtx.lock_shared(); // 加读锁std::cout << "Reader " << id << " read: " << shared_data << std::endl;rw_mtx.unlock_shared(); // 解读锁std::this_thread::sleep_for(std::chrono::milliseconds(100));}
}void writer(int id) {for (int i = 0; i < 5; ++i) {rw_mtx.lock(); // 加写锁++shared_data;std::cout << "Writer " << id << " wrote: " << shared_data << std::endl;rw_mtx.unlock(); // 解写锁std::this_thread::sleep_for(std::chrono::milliseconds(100));}
}int main() {std::thread readers[3];std::thread writers[2];for (int i = 0; i < 3; ++i) readers[i] = std::thread(reader, i);for (int i = 0; i < 2; ++i) writers[i] = std::thread(writer, i);for (int i = 0; i < 3; ++i) readers[i].join();for (int i = 0; i < 2; ++i) writers[i].join();return 0;
}

特点

  • 读共享:多个读线程可以同时持有读锁。

  • 写独占:写锁是独占的,写线程持有锁时,其他线程不能获取读锁或写锁。

适用场景

  • 读多写少:适用于读操作远多于写操作的场景,如缓存系统、数据库。

  • 读操作不修改共享资源:读操作不会修改共享资源,因此可以并发执行。

为什么适用

  • 实现机制:读写锁通过计数器跟踪读锁的数量,写锁需要等待所有读锁释放。

  • 性能特点:在读多写少的场景中,读写锁可以显著提高并发性能。


3. 自旋锁(Spinlock)

代码示例

#include <iostream>
#include <thread>
#include <atomic>std::atomic_flag spinlock = ATOMIC_FLAG_INIT; // 定义一个自旋锁
int shared_data = 0;void increment() {for (int i = 0; i < 100000; ++i) {while (spinlock.test_and_set(std::memory_order_acquire)); // 自旋等待++shared_data; // 访问共享资源spinlock.clear(std::memory_order_release); // 释放锁}
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "Final value of shared_data: " << shared_data << std::endl;return 0;
}

特点

  • 忙等待:线程不会进入睡眠状态,而是不断检查锁的状态。

  • 低延迟:适用于锁持有时间非常短的场景,避免了线程切换的开销。

  • 高 CPU 占用:如果锁持有时间较长,会导致 CPU 资源的浪费。

适用场景

  • 锁持有时间非常短:适用于锁持有时间非常短的场景,如内核中的中断处理程序。

  • 多核处理器上的高并发场景:在多核处理器上,自旋锁可以避免线程切换的开销。

为什么适用

  • 实现机制:自旋锁通过忙等待实现,不会让线程进入睡眠状态。

  • 性能特点:适合锁持有时间非常短的场景,因为忙等待的代价低于线程切换的开销。


4. 信号量(Semaphore)

代码示例

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>class Semaphore {
public:Semaphore(int count = 0) : count(count) {}void acquire() {std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, [this]{ return count > 0; });--count;}void release() {std::lock_guard<std::mutex> lock(mtx);++count;cv.notify_one();}private:std::mutex mtx;std::condition_variable cv;int count;
};Semaphore sem(2); // 初始化信号量,允许 2 个线程同时访问void task(int id) {sem.acquire();std::cout << "Task " << id << " is running!" << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));sem.release();
}int main() {std::thread t1(task, 1);std::thread t2(task, 2);std::thread t3(task, 3);t1.join();t2.join();t3.join();return 0;
}

特点

  • 计数机制:信号量维护一个计数器,用于控制对共享资源的访问数量。

  • 阻塞等待:如果计数器为 0,线程会阻塞等待。

适用场景

  • 限制并发访问数量:适用于资源池管理(如线程池、连接池)。

  • 生产者-消费者模型:用于控制生产者和消费者的并发数量。

为什么适用

  • 实现机制:信号量通过计数器和条件变量实现,可以灵活控制并发访问数量。

  • 性能特点:适合需要限制并发访问数量的场景。


5. 条件变量(Condition Variable)

代码示例

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>std::mutex mtx;
std::condition_variable cv;
bool ready = false;void worker() {std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, []{ return ready; }); // 等待条件满足std::cout << "Worker is running!" << std::endl;
}int main() {std::thread t(worker);std::this_thread::sleep_for(std::chrono::seconds(2));{std::lock_guard<std::mutex> lock(mtx);ready = true;}cv.notify_one(); // 通知等待的线程t.join();return 0;
}

特点

  • 条件同步:用于线程间的条件同步。

  • 阻塞等待:线程会阻塞等待条件满足。

适用场景

  • 复杂的同步需求:如生产者-消费者模型。

  • 线程间协作:适用于需要线程间协作的场景。

为什么适用

  • 实现机制:条件变量通过阻塞和唤醒机制实现线程间同步。

  • 性能特点:适合需要复杂同步的场景。


6. synchronized(Java 中的锁机制)

代码示例

public class Counter {private int count = 0;public synchronized void increment() {count++;}public int getCount() {return count;}public static void main(String[] args) throws InterruptedException {Counter counter = new Counter();Thread t1 = new Thread(() -> {for (int i = 0; i < 100000; i++) {counter.increment();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 100000; i++) {counter.increment();}});t1.start();t2.start();t1.join();t2.join();System.out.println("Final count: " + counter.getCount());}
}

特点

  • 内置锁:Java 中的 synchronized 关键字提供了一种简单的锁机制。

  • 可重入:同一个线程可以多次获取锁。

适用场景

  • 简单的同步需求:适用于不需要复杂锁机制的场景。

  • 单线程重入:适用于需要重入锁的场景。

为什么适用

  • 实现机制synchronized 通过 JVM 内置的锁机制实现,简单易用。

  • 性能特点:适合简单的同步需求。


总结

锁类型特点适用场景为什么适用
Mutex独占访问,阻塞等待保护临界区,锁持有时间较长的场景通过阻塞机制实现,适合锁持有时间较长的场景
RW Lock读共享,写独占读多写少的场景,如缓存系统、数据库通过计数器实现读共享,适合读多写少的场景
自旋锁忙等待,不阻塞线程锁持有时间非常短的场景,多核处理器上的高并发场景通过忙等待实现,适合锁持有时间非常短的场景
信号量控制对共享资源的访问数量资源池管理,限制同时访问共享资源的线程数量通过计数器和条件变量实现,适合需要限制并发访问数量的场景
条件变量线程间条件同步复杂的同步需求,如生产者-消费者模型通过阻塞和唤醒机制实现,适合需要复杂同步的场景
synchronized内置锁,简单易用简单的同步需求,单线程重入通过 JVM 内置锁机制实现,适合简单的同步需求

通过代码示例和实现机制的分析,可以更好地理解各种锁的适用场景和性能特点。选择合适的锁类型取决于具体的应用场景和性能需求。

相关文章:

常见锁类型介绍

下面结合代码详细介绍 Mutex、RW Lock、Futex、自旋锁、信号量、条件变量 和 synchronized&#xff0c;并分析它们的适用场景、特点以及为什么这些锁适用于特定场景。我们将从锁的实现机制和性能特点出发&#xff0c;解释其适用性。 1. Mutex&#xff08;互斥锁&#xff09; 代…...

Java中,Scanner和System.out超时的解决方法及原理

ACM 模式的原理 在输入输出的时候&#xff0c;会先将输入输出的东西放在一个文件里&#xff0c;这个文件也叫做 IO 设备 为什么 Scanner 会慢 new 一个 Scanner &#xff0c;在 Scanner 里面调用 next 的时候&#xff0c;程序会直接访问 IO 设备。在调用一个 next 的时候&…...

一种数据高效具身操作的原子技能库构建方法

25年1月来自京东、中科大、深圳大学、海尔集团、地平线机器人和睿尔曼智能科技的论文“An Atomic Skill Library Construction Method for Data-Efficient Embodied Manipulation”。 具身操控是具身人工智能领域的一项基本能力。尽管目前的具身操控模型在特定场景下表现出一定…...

云创智城YunCharge 新能源二轮、四轮充电解决方案(云快充、万马爱充、中电联、OCPP1.6J等多个私有单车、汽车充电协议)之新能源充电行业系统说明书

云创智城YunCharge 新能源充电行业系统说明书 ⚡官方文档 ⚡官网地址 1. 引言 随着全球环境保护和能源危机的加剧&#xff0c;新能源汽车行业得到了快速发展&#xff0c;充电基础设施建设也随之蓬勃发展。新能源充电行业系统旨在提供高效、便捷的充电服务&#xff0c;满足电…...

JVM垃圾回收器深度底层原理分析与知识体系构建

一、垃圾回收的基本步骤 标记&#xff08;Marking&#xff09; 从GC Roots&#xff08;如虚拟机栈、方法区静态变量、本地方法栈等&#xff09;出发&#xff0c;遍历对象引用链&#xff0c;标记所有可达对象为存活对象&#xff0c;未被标记的则视为垃圾。此阶段需暂停用户线程&…...

30.[前端开发-JavaScript基础]Day07-数组Array-高阶函数-日期Date-DOM

JavaScript的DOM操作 &#xff08;一&#xff09; 1 什么是DOM&#xff1f; 认识DOM和BOM 深入理解DOM 2 认识DOM Tree DOM Tree的理解 3 DOM的整体结构 DOM的学习顺序 DOM的继承关系图 document对象 4 节点、元素导航 节点&#xff08;Node&#xff09;之间的导航&…...

IP、网关、子网掩码、DNS 之间的关系详解

IP、网关、子网掩码、DNS 之间的关系详解 在计算机网络中&#xff0c;IP、网关、子网掩码和 DNS 是几个关键概念&#xff0c;它们协同工作&#xff0c;共同保障网络通信的顺畅。本文将详细探讨它们之间的关系。 一、IP 地址 IP 地址是网络中设备的唯一标识&#xff0c;如同现…...

【Day50 LeetCode】图论问题 Ⅷ

一、图论问题 Ⅷ 1、dijkstra算法 堆优化 采用堆来优化&#xff0c;适合节点多的稀疏图。代码如下&#xff1a; # include<iostream> # include<vector> # include<list> # include<queue> # include<climits>using namespace std;class myco…...

结构体介绍及内存大小分配问题

结构体 一.结构体的介绍1.1结构体的声明1.2匿名结构体1.3结构的自引用1.4使用 typedef 简化结构体类型名 二.结构体内存对齐2.1内存对齐规则2.2结构体内存对齐原因2.3修改默认对齐数 在 C 语言中&#xff0c;结构体&#xff08;struct&#xff09;是一种用户自定义的数据类型&a…...

halcon 条形码、二维码识别、opencv识别

一、条形码 函数介绍 create_bar_code_model * 1.创建条码读取器的模板 * 参数一&#xff1a;通用参数的名称&#xff0c;针对条形码模型进行调整。默认值为空 * 参数二&#xff1a;针对条形码模型进行调整 * 参数三&#xff1a;条形码模型的句柄。 create_bar_code_model (…...

Vue框架的使用 搭建打包 Vue的安全问题(Xss,源码泄露)

前言 什么是Vue&#xff1f; Vue是轻量级的js框架 可以帮助我们一键构造网站&#xff0c;打包app程序等 Vue的基本使用 1、构造框架并启用 新建一个 目录 使用终端切换到当前的目录 创建vue项目 第一个弹出使用语法我们选择是 剩下的全选择否 发现创建好了 接着进行…...

Java+SpringBoot+Vue+数据可视化的音乐推荐与可视化平台(程序+论文+讲解+安装+调试+售后)

感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;我会一一回复&#xff0c;希望帮助更多的人。 系统介绍 在互联网技术以日新月异之势迅猛发展的浪潮下&#xff0c;5G 通信技术的普及、云计算能力…...

day2 - SpringBoot框架开发技术

主要内容 1. SpringBoot简介 2. 构建springboot工程 3. springboot接口返回json 4. springboot热部署 5. springboot资源属性配置 6. springboot整合模板引擎 7. springboot异常处理 8. springboot整合MyBatis 9. springboot整合redis 10. springboot整合定时任务 11. springbo…...

Flash-03

1-问题&#xff1a;Flash软件画两个图形&#xff0c;若有部分重合则变为一个整体 解决方法1&#xff1a;两个图形分属于不同的图层 解决方法2&#xff1a;将每个图形都转化为【元件】 问题2&#xff1a;元件是什么&#xff1f; 在 Adobe Flash&#xff08;现在称为 Adobe Anim…...

新建菜单项的创建之CmpGetValueListFromCache函数分析

第一部分&#xff1a; PCELL_DATA CmpGetValueListFromCache( IN PHHIVE Hive, IN PCACHED_CHILD_LIST ChildList, OUT BOOLEAN *IndexCached, OUT PHCELL_INDEX ValueListToRelease ) 0: kd> dv KeyControlBlock 0xe1…...

【Word2Vec】Skip-gram 的直观理解(深入浅出)

01 什么是skip-gram 一句话来说就是&#xff0c;给定中心词&#xff0c;然后预测其周围的词&#xff1a; 02 模型结构 对于skip-gram来说&#xff0c;输入是一个[1 x V]维的ont-hot向量&#xff0c;其中V为词表大小&#xff0c;值为1的那一项就表示我们的中心词。经过一个[V x…...

在MacOS上打造本地部署的大模型知识库(一)

一、在MacOS上安装Ollama docker run -d -p 3000:8080 --add-hosthost.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main 最后停掉Docker的ollama&#xff0c;就能在webui中加载llama模…...

(21)从strerror到strtok:解码C语言字符函数的“生存指南2”

❤个人主页&#xff1a;折枝寄北的博客 ❤专栏位置&#xff1a;简单入手C语言专栏 目录 前言1. 错误信息报告1.1 strerror 2. 字符操作2.1 字符分类函数2.2 字符转换函数 3. 内存操作函数3.1 memcpy3.2 memmove3.2memset3.3 memcmp 感谢您的阅读 前言 当你写下strcpy(dest, s…...

DeepSeek推出DeepEP:首个开源EP通信库,让MoE模型训练与推理起飞!

今天&#xff0c;DeepSeek 在继 FlashMLA 之后&#xff0c;推出了第二个 OpenSourceWeek 开源项目——DeepEP。 作为首个专为MoE&#xff08;Mixture-of-Experts&#xff09;训练与推理设计的开源 EP 通信库&#xff0c;DeepEP 在EP&#xff08;Expert Parallelism&#xff09…...

1.2 Kaggle大白话:Eedi竞赛Transformer框架解决方案02-GPT_4o生成训练集缺失数据

目录 0. 本栏目竞赛汇总表1. 本文主旨2. AI工程架构3. 数据预处理模块3.1 配置数据路径和处理参数3.2 配置API参数3.3 配置输出路径 4. AI并行处理模块4.1 定义LLM客户端类4.2 定义数据处理函数4.3 定义JSON保存函数4.4 定义数据分片函数4.5 定义分片处理函数4.5 定义文件名排序…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

python打卡day49

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统&#xff0c;支持多语言对话&#xff08;如 中文&#xff0c;英文&#xff0c;日语&#xff09;&#xff0c;语音情感&#xff08;如 开心&#xff0c;悲伤&#xff09;&#x…...

自然语言处理——Transformer

自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效&#xff0c;它能挖掘数据中的时序信息以及语义信息&#xff0c;但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN&#xff0c;但是…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

Ubuntu Cursor升级成v1.0

0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开&#xff0c;快捷键也不好用&#xff0c;当看到 Cursor 升级后&#xff0c;还是蛮高兴的 1. 下载 Cursor 下载地址&#xff1a;https://www.cursor.com/cn/downloads 点击下载 Linux (x64) &#xff0c;…...

什么是VR全景技术

VR全景技术&#xff0c;全称为虚拟现实全景技术&#xff0c;是通过计算机图像模拟生成三维空间中的虚拟世界&#xff0c;使用户能够在该虚拟世界中进行全方位、无死角的观察和交互的技术。VR全景技术模拟人在真实空间中的视觉体验&#xff0c;结合图文、3D、音视频等多媒体元素…...