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

Redis底层数据结构之深入理解跳表(2)

        上一篇文章中我们详细讲述了跳表的增添、查找和修改的操作,这篇文章我们来讲解一下跳表在多线程并发时的安全问题。在Redis中,除了网络IO部分和大文件的后台复制涉及到多线程外,其余任务执行时全部都是单线程,这也就意味着在Redis中不存在跳表的多线程并发问题。但在Java程序开发中,我们可能也会使用到跳表,这时候就不得不考虑如何设计一个并发安全的调表结构。

        JDK中提供了一种支持并发安全的跳表结构ConcerrentSkipListMap可以直接使用。不过这篇文章我们来自己思考一下在设计并发安全的跳表时我们需要考虑哪些内容。

全局读写锁

        首先对于跳表的操作可以分为读操作和写操作两种。那么最简单粗暴的方法就是对整个跳表加读写锁。所有的写操作需要持有写锁才可以进行,所有的读操作可以同时持有多把读锁,来实现并发读取。不过这时候我们来考虑这样一个问题,对跳表加了全局读写锁,如果一个线程拿到了写锁,并且对跳表的操作时间很长,那么后边所有的写操作和读操作都会被阻塞,如果对跳表的大小、存储数据的大小和每次写操作的时间进行严格限制,全局加读写锁在写少读多的场景下还是有一定的可用性的。

        上面全局加读写锁虽然实现简单,但是有很大可能会造成线程间的阻塞等待,实用性并不高,而造成阻塞问题的根源是锁的颗粒度太大了,有没有一种方法可以将锁的颗粒度进行细化,这样就可以同时允许多个线程拿到不同的锁进行操作,降低线程阻塞的概率。

节点读写锁

        我们来思考这样一个问题,当我们对单向链表并发操作时,对于写操作(增加、删除),其实只需要执行一步操作,就是找到被操作节点的前一个节点,并将其next修改为被操作节点的后一个节点。这就意味着,如果我们在并发环境下对被操作节点的前一个节点进行加锁操作,就可以保证同一时间只有一个线程对被加锁节点后的节点进行操作。

        在对跳表的节点进行加锁时,我们还需要多考虑一点,由于新加入的节点的高度是随机的,可能会比当前跳表的高度要高,这个时候我们需要对跳表的头结点进行加锁,并修改跳表的最大高度。

        总的来说,其实我们在细化跳表中锁的颗粒度到节点上时,需要考虑两部分。第一是如果新加入的节点高度小于跳表原最大高度,这时就从上至下逐层取到操作节点的左边界节点的锁即可;第二是如果新加入的节点高度大于跳表原有最大高度,这时就需要持有跳表的头结点锁,然后再进行第一部分的操作。

查询操作

        查询操作的步骤就是从跳表的最高层head节点开始,从上往下逐层遍历,直到找到key值小于target并且最接近于target的左边界节点,然后进行层数下降。具体的流程如下图所示:

增添操作

        增加操作执行时,如果增加的节点已经存在了,则会更新为新值,如果不存在,则要将其插入跳表中。可以将增加操作分为以下四个步骤:

        ①首先检查要添加的节点是否存在。

        ②如果存在,则将旧值更新为新值。

        ③如果不存在,则将新节点插入跳表中。

        ④如果要插入节点,并且节点高度比原有跳表要高,则对跳表的高度进行扩容。

        上述步骤中的①、②、③在并发情况下需要是原子性的,来保证单一线程对跳表更改的有效性。具体的流程如下图所示:

删除操作

        删除操作的步骤就是查询到要删除的节点,将其每一层的前一个节点的指针进行变更,指向当前层被操作节点的后一个节点就可以了。具体的流程如下所示:

        不过删除操作存在一个问题,就是左边界这种策略对于删除和增添并发执行的时候会失效,可能会造成新增添的节点被误删。所以造执行删除操作时,还是采用全局锁的方式来对其和增添操作进行隔离。

        本文主要梳理了一下自己实现并发安全的跳表的一些思路,算是学完Redis底层数据结构和并发编程之后笔者的一些思考,如果有什么问题或者勘误,欢迎评论区留言。

        

相关文章:

Redis底层数据结构之深入理解跳表(2)

上一篇文章中我们详细讲述了跳表的增添、查找和修改的操作,这篇文章我们来讲解一下跳表在多线程并发时的安全问题。在Redis中,除了网络IO部分和大文件的后台复制涉及到多线程外,其余任务执行时全部都是单线程,这也就意味着在Redis…...

大模型编程助手-Cline

官网: https://cline.bot/ Cline 是一款深度集成在 Visual Studio Code(VSCode) 中的开源 AI 编程助手插件,旨在通过结合大语言模型(如 Claude 3.5 Sonnet、DeepSeek V3、Google Gemini 等)和工具链&#…...

[蓝桥杯]兰顿蚂蚁

兰顿蚂蚁 题目描述 兰顿蚂蚁,是于 1986 年,由克里斯兰顿提出来的,属于细胞自动机的一种。 平面上的正方形格子被填上黑色或白色。在其中一格正方形内有一只"蚂蚁"。 蚂蚁的头部朝向为:上下左右其中一方。 蚂蚁的移…...

使用 Python 构建并调用 ComfyUI 图像生成 API:完整实战指南

快速打造你自己的本地 AI 图像生成服务,支持 Web 前端一键调用! 📌 前言 在 AIGC 快速发展的今天,ComfyUI 作为一款模块化、节点式的图像生成界面,备受开发者青睐。但默认情况下,ComfyUI 主要通过界面交互…...

嵌入式学习笔记-freeRTOS taskENTER_CRITICAL(_FROM_ISR)跟taskEXIT_CRITICAL(_FROM_ISR)函数解析

一 函数taskENTER_CRITICAL,taskEXIT_CRITICAL 函数taskENTER_CRITICAL最终实现如下: 第①处按照系统设定的configMAX_SYSCALL_INTERRUPT_PRIORITY值对中断进行屏蔽 第②处调用一次自增一次 第③处检查中断状态寄存器位,如果有任何中断位置…...

Unity基础-数学向量

Unity基础-数学向量 二、向量相关用法 概述 向量在Unity游戏开发中扮演着重要角色,用于表示位置、方向、速度等。Unity提供了Vector2、Vector3等结构体来处理向量运算。 1. 向量基础操作 1.1 向量创建和访问 // 创建向量 Vector3 position new Vector3(1, 2,…...

【华为云Astro-服务编排】服务编排中图元的使用与配置

目录 子服务编排图元 子服务编排图元的作用 如何使用子服务编排图元 脚本图元 脚本图元的作用 如何使用脚本图元 记录创建图元 记录创建图元的作用 如何使用记录创建图元 记录删除图元 记录删除图元的作用 如何使用记录删除图元 记录查询图元 记录查询图元的作用…...

1panel面板中部署SpringBoot和Vue前后端分离系统 【图文教程】

1panel面板中部署SpringBoot和Vue前后端分离系统 一,1panel面板部署二,安装OpenResty三,安装MySQL,Redis等Spring boot 运行依赖环境四,SpringBoot 应用配置及打包部署配置打包部署 五 ,前端VUE应用配置打包…...

C++.OpenGL (7/64)摄像机(Camera)

摄像机(Camera) 摄像机系统核心组件 #mermaid-svg-lmysTXAyyzKytiOC {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-lmysTXAyyzKytiOC .error-icon{fill:#552222;}#mermaid-svg-lmysTXAyyzKytiOC .error-text{fi…...

使用xdocreport导出word

之前java总用freemaker进行导出&#xff0c;但是改xml实在是太繁琐了&#xff0c;这次找了另一个工具进行体验. 一、简单导出 pom引入 <dependency><groupId>fr.opensagres.xdocreport</groupId><artifactId>fr.opensagres.xdocreport.core</arti…...

青少年编程与数学 01-011 系统软件简介 05 macOS操作系统

青少年编程与数学 01-011 系统软件简介 05 macOS操作系统 一、历史发展&#xff08;一&#xff09;经典 Mac OS&#xff08;1984-2001&#xff09;&#xff08;二&#xff09;Mac OS X&#xff08;2001-2016&#xff09;&#xff08;三&#xff09;macOS&#xff08;2016-至今&…...

Python打卡训练营学习记录Day43

作业&#xff1a; kaggle找到一个图像数据集&#xff0c;用cnn网络进行训练并且用grad-cam做可视化 进阶&#xff1a;并拆分成多个文件 从谷歌图片中拍摄的 10 种不同类别的动物图片 数据预处理 import os from torchvision import datasets, transforms from torch.utils…...

【Android基础回顾】二:handler消息机制

Android 的 Handler 机制 是 Android 应用中实现线程间通信、任务调度、消息分发的核心机制之一&#xff0c;它基于 消息队列&#xff08;MessageQueue&#xff09; 消息循环&#xff08;Looper&#xff09; 消息处理器&#xff08;Handler&#xff09; 组成。 1 handler的使用…...

每日Prompt:每天上班的状态

提示词 一个穿着清朝官服的僵尸脸上贴着符纸&#xff0c;在电脑面前办公&#xff0c;房间阴暗&#xff0c;电脑桌面很乱&#xff0c;烟灰缸里面满是烟头...

.net ORM框架dapper批量插入

.NET ORM 框架 Dapper 批量插入全解析 在 .NET 开发中&#xff0c;与数据库交互是常见需求。Dapper 作为轻量级的 ORM&#xff08;对象关系映射&#xff09;库&#xff0c;在简化数据库交互方面表现出色。今天我们就来深入探讨 Dapper 实现批量插入的几种方法。 为什么需要批…...

C++11 右值引用:从入门到精通

文章目录 一、引言二、左值和右值&#xff08;一&#xff09;概念&#xff08;二&#xff09;区别和判断方法 三、左值引用和右值引用&#xff08;一&#xff09;左值引用&#xff08;二&#xff09;右值引用 四、移动语义&#xff08;一&#xff09;概念和必要性&#xff08;二…...

.net 使用MQTT订阅消息

在nuGet下载M2Mqtt V4.3.0版本。&#xff08;支持.net framework&#xff09; 订阅主题 public void LoadMQQCData() {string enpoint "xxx.xxx.x.x";//ip地址int port 1883;//端口string user "usrname";//用户名string pwd "pwd";//密码…...

Python实现快速排序的三种经典写法及算法解析

今天想熟悉一下python的基础写法&#xff0c;那就从最经典的快速排序来开始吧&#xff1a; 1、经典分治写法&#xff08;原地排序&#xff09; 时间复杂度&#xff1a;平均O(nlogn)&#xff0c;最坏O(n) 空间复杂度&#xff1a;O(logn)递归栈空间 特点&#xff1a;通过左右指针…...

【递归、搜索与回溯】综合练习(四)

&#x1f4dd;前言说明&#xff1a; 本专栏主要记录本人递归&#xff0c;搜索与回溯算法的学习以及LeetCode刷题记录&#xff0c;按专题划分每题主要记录&#xff1a;&#xff08;1&#xff09;本人解法 本人屎山代码&#xff1b;&#xff08;2&#xff09;优质解法 优质代码…...

强化学习入门:Gym实现CartPole随机智能体

前言 最近想开一个关于强化学习专栏&#xff0c;因为DeepSeek-R1很火&#xff0c;但本人对于LLM连门都没入。因此&#xff0c;只是记录一些类似的读书笔记&#xff0c;内容不深&#xff0c;大多数只是一些概念的东西&#xff0c;数学公式也不会太多&#xff0c;还望读者多多指教…...

STM32:CAN总线精髓:特性、电路、帧格式与波形分析详解

声明&#xff1a;此博客是我的学习笔记&#xff0c;所看课程是江协科技的CAN总线课程&#xff0c;知识点都大同小异&#xff0c;我仅进行总结并加上了我自己的理解&#xff0c;所引案例也都是课程中的案例&#xff0c;希望对你的理解有所帮助&#xff01; 知识点1【CAN总线的概…...

贝叶斯深度学习!华科大《Nat. Commun.》发表BNN重大突破!

华科大提出基于贝叶斯深度学习的超分辨率成像&#xff0c;成功被Nat. Commun.收录。可以说&#xff0c;这是贝叶斯神经网络BNN近期最值得关注的成果之一了。另外还有AAAI 2025上的Bella新框架&#xff0c;计算成本降低了99.7%&#xff0c;也非常值得研读。 显然鉴于BNN“不确定…...

【大模型LLM学习】Flash-Attention的学习记录

【大模型LLM学习】Flash-Attention的学习记录 0. 前言1. flash-attention原理简述2. 从softmax到online softmax2.1 safe-softmax2.2 3-pass safe softmax2.3 Online softmax2.4 Flash-attention2.5 Flash-attention tiling 0. 前言 Flash Attention可以节约模型训练和推理时间…...

三、元器件的选型

前言&#xff1a;我们确立了题目的功能后&#xff0c;就可以开始元器件的选型&#xff0c;元器件的选型关乎到我们后面代码编写的一个难易。 一、主控的选择 主控的选择很大程度上决定我们后续使用的代码编译器&#xff0c;比如ESP32使用的是VScode&#xff0c;或者Arduino&a…...

精益数据分析(95/126):Socialight的定价转型启示——B2B商业模式的价格策略与利润优化

精益数据分析&#xff08;95/126&#xff09;&#xff1a;Socialight的定价转型启示——B2B商业模式的价格策略与利润优化 在创业过程中&#xff0c;从B2C转向B2B不仅是商业模式的转变&#xff0c;更是定价策略与成本结构的全面重构。今天&#xff0c;我们将通过Socialight的实…...

stm32_DMA

DMA 1. 概念与基本原理 DMA&#xff0c;全称Direct Memory Access&#xff0c;即直接存储器访问。它是微控制器&#xff08;MCU&#xff09;、嵌入式处理器中的一个独立硬件模块&#xff0c;用于在无需CPU干预的情况下&#xff0c;在不同内存区域&#xff08;包括外设寄存器和…...

物联网数据归档之数据存储方案选择分析

在上一篇文章中《物联网数据归档方案选择分析》中凯哥分析了归档设计的两种方案,并对两种方案进行了对比。这篇文章咱们就来分析分析,归档后数据应该存储在哪里?及存储方案对比。 这里就选择常用的mysql及taos数据库来存储归档后的数据吧。 你在处理设备归档表存储方案时对…...

【自动驾驶避障开发】如何让障碍物在 RViz 中‘显形’?呈现感知数据转 Polygon 全流程

【自动驾驶避障开发】如何让障碍物在 RViz 中"显形"?呈现感知数据转 Polygon 全流程 自动驾驶系统中的障碍物可视化是开发调试过程中至关重要的一环。本文将详细介绍如何将自动驾驶感知模块检测到的障碍物数据转换为RViz可显示的Polygon(多边形)形式,实现障碍物…...

【C语言】C语言经典小游戏:贪吃蛇(上)

文章目录 一、游戏背景及其功能二、Win32 API介绍1、Win32 API2、控制台程序3、定位坐标&#xff08;COORD&#xff09;4、获得句柄&#xff08;GetStdHandle&#xff09;5、获得光标属性&#xff08;GetConsoleCursorInfo&#xff09;1&#xff09;描述光标属性&#xff08;CO…...

usbutils工具的使用帮助

作为嵌入式系统开发中的常用工具&#xff0c;usbutils 是一套用于管理和调试USB设备的Linux命令行工具集。以下是其核心功能和使用方法的详细说明&#xff1a; 1. 工具组成 核心命令&#xff1a; lsusb&#xff1a;列出所有连接的USB设备及详细信息&#xff08;默认安装&#…...