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

分布式锁简介

0f9d713ccaa143168696d6acce41fda9.jpgRedis因为单进程、性能高常被用于分布式锁;锁在程序中作用是同步工具,保证共享资源在同一时刻只能被一个线程访问。

 

 

Java中经常用的锁synchronized、Lock,但是Java的锁智能保证单机的时候有效,分布式集群环境就无能为力了,这时候需要用到分布式锁。

 

分布式锁,就是分布式项目开发中用到的锁,用来控制分布式系统之间同步访问共享资源,一般来说,分布式锁满足几个特性:

 

1. 互斥性:在任何时刻,对于同一条数据,只有一台应用可以获取到分布式锁;

2. 高可用性:在分布式场景下,一小部分服务器宕机不影响正常使用,这种情况就需要将提供分布式锁的服务以集群的方式部署;

3. 防止锁超时:如果客户端没有主动释放锁,服务器会在一段时间之后自动释放锁,避免死锁的产生;

4. 独占性:加锁解锁必须由一台服务器惊醒,也就是锁的持有者才可以释放锁;

5. 可重入性:在同一个节点进程内,同一个线程可多次获取锁;

实现分布式锁的工具还有db、zookeeper、RedisLockRegistry,但操作大致也是:加锁、解锁、锁超时。

 

实现锁的命令

1. setnx(set if not exists),setnx key value;设置成功返回1,否则返回0;

 

问题:为了防止致命的问题,key没有过期时间,除非手动删除key或者获取锁后设置过期时间,不然其他线程永远拿不到锁;

 

解决:给key加过期时间,让线程获取锁的时候并且设置过期时间;

 

问题:加锁、锁超时分两步不是原子性操作,可能获取锁成功但设置时间失败;

 

2. setex,setex key seconds value;将值value关联到Key,并将Key的生存时间设为seconds(以秒为单位)。如果key存在,setex命令将覆写旧值;这两步是原子性会在同一时间完成;

 

3. psetex,psetex key milliseconds value,与setex相似,以毫秒为单位设置key的生存时间;

 

从Redis 2.6.12版本开始,set命令可以通过参数来实现setnx,setex,psetex三个命令相同的效果,如set key value nx ex seconds

 

伪代码工具类实现锁的基础方法

public class RedisLockUtil {

 

    private String LOCK_KEY = "redis_lock";

 

    // key的持有时间,5ms

    private long EXPIRE_TIME = 5;

 

    // 等待超时时间,1s

    private long TIME_OUT = 1000;

 

    // redis命令参数,相当于nx和px的命令合集

    private SetParams params = SetParams.setParams().nx().px(EXPIRE_TIME);

 

    // redis连接池,连的是本地的redis客户端

    JedisPool jedisPool = new JedisPool("127.0.0.1", 6379);

 

    /**

     * 加锁

     *

     * @param id

     * 线程的id,或者其他可识别当前线程且不重复的字段

     * @return

     */

    public boolean lock(String id) {

        Long start = System.currentTimeMillis();

        Jedis jedis = jedisPool.getResource();

        try {

            for (;;) {

                // SET命令返回OK ,则证明获取锁成功

                String lock = jedis.set(LOCK_KEY, id, params);

                if ("OK".equals(lock)) {

                    return true;

                }

                // 否则循环等待,在TIME_OUT时间内仍未获取到锁,则获取失败

                long l = System.currentTimeMillis() - start;

                if (l >= TIME_OUT) {

                    return false;

                }

                try {

                    // 休眠一会,不然反复执行循环会一直失败

                    Thread.sleep(100);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        } finally {

            jedis.close();

        }

    }

 

    /**

     * 解锁

     *

     * @param id

     * 线程的id,或者其他可识别当前线程且不重复的字段

     * @return

     */

    public boolean unlock(String id) {

        Jedis jedis = jedisPool.getResource();

        // 删除key的lua脚本

        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then" + " return redis.call('del',KEYS[1]) " + "else"

            + " return 0 " + "end";

        try {

            String result =

                jedis.eval(script, Collections.singletonList(LOCK_KEY), Collections.singletonList(id)).toString();

            return "1".equals(result);

        } finally {

            jedis.close();

        }

    }

}

测试demo

 

public class RedisLockDemo {

    private static RedisLockUtil demo = new RedisLockUtil();

    private static Integer NUM = 101;

 

    public static void main(String[] args) {

        for (int i = 0; i < 100; i++) {

            new Thread(() -> {

                String id = Thread.currentThread().getId() + "";

                boolean isLock = demo.lock(id);

                try {

                 // 拿到锁的话,就对共享参数减一

                    if (isLock) {

                        NUM--;

                        System.out.println(NUM);

                    }

                } finally {

                 // 释放锁一定要注意放在finally

                    demo.unlock(id);

                }

            }).start();

        }

    }

}

//100

//99

//98

//...

一个健全的分布式锁要考虑的方面很多,一般使用开源工具(zookeepre,db,Redisson等)

 

Redis实现分布式锁的缺陷

客户端长时间阻塞导致锁失效问题

客户端1的到锁,因网络问题或gc等原因导致长时间阻塞,然后业务程序还没执行完就过期了,这时候客户端2也能正常拿到锁,可能会导致线程安全问题。

 

非原子性操作

误删锁

项目中常使用的Redis分布式锁

RedisLockRegistry是 Spring-Integration 集成工具包项目提供的基于 Redis 的分布式锁管理器

 

基于 Redis 的分布式锁实现,主要是依托 get 和 setnx 的方法,再包裹一层本地的可重入锁实现。

相关文章:

分布式锁简介

Redis因为单进程、性能高常被用于分布式锁&#xff1b;锁在程序中作用是同步工具&#xff0c;保证共享资源在同一时刻只能被一个线程访问。 Java中经常用的锁synchronized、Lock&#xff0c;但是Java的锁智能保证单机的时候有效&#xff0c;分布式集群环境就无能为力了&#xf…...

【嵌入式Linux学习笔记】Linux驱动开发

Linux系统构建完成后&#xff0c;就可以基于该环境方便地进行开发了&#xff0c;相关的开发流程与MCU类似&#xff0c;但是引入了设备树的概念&#xff0c;编写应用代码要相对复杂一点。但是省去了很多配置工作。 学习视频地址&#xff1a;【正点原子】STM32MP157开发板 字符…...

2023年中国高校计算机大赛-团队程序设计天梯赛(GPLT)上海理工大学校内选拔赛(同步赛)(H题)(线段树)

又到了万物复苏的季节&#xff0c;家乡的苹果树结果了。像往常一样小龙同学被叫回家摘苹果。 假设需要采摘的一棵树上当前有a颗苹果&#xff0c;那么小龙会采摘⌈a/3⌉颗苹果&#xff0c;其中⌈x⌉表示不小于x的最小整数。 但是&#xff0c;为了可持续发展&#xff0c;若a小于1…...

Linux内核Thermal框架详解十三、Thermal Governor(3)

接前一篇文章Linux内核Thermal框架详解十二、Thermal Governor&#xff08;2&#xff09; 二、具体温控策略 上一篇文章介绍并详细分析了bang_bang governor的源码。本文介绍第2种温控策略&#xff1a;fair_share。 2. fair_share fair_share governor总的策略是频率档位⽐较…...

TikTok品牌出海创世纪(二)

目录 1.推荐算法打造王者品牌 2.品牌聚焦海外Z群体 3.持续扩展应用场景 加速品牌全球化传播 品牌聚焦海外Z群体 “这个地球上&#xff0c;三分之二的人都在用Facebook“&#xff0c;这是对Facebook曾经统治地位最直观的描述。 但如今&#xff0c;这家全球社交媒体巨头的光环正…...

iOS中SDK开发 -- cocoapods库创建

在iOS项目中&#xff0c;经常使用cocoadpods来进行依赖管理以及三方库引入等。引入的三方库一般会有几种形式&#xff1a;一、在Pods目录下可以直接看到源代码的开源库&#xff0c;如AFNetworking&#xff0c;Masonry等常见开源库。二、在Pods目录下拉取的项目文件只能看到对应…...

2023年了,还是没学会内卷....

先做个自我介绍&#xff1a;我&#xff0c;普本&#xff0c;通信工程专业&#xff0c;现在飞猪干软件测试&#xff0c;工作时长两年半。 回望疫情纪元&#xff0c;正好是实习 毕业这三年。要说倒霉也是真倒霉&#xff0c;互联网浪潮第三波尾巴也没抓住&#xff0c;狗屁造富神…...

chatGPT爆火,什么时候中国能有自己的“ChatGPT“

目录 引言 一、ChatGPT爆火 二、中国何时能有自己的"ChatGPT" 三、为什么openai可以做出chatGPT? 四、结论 引言 随着人工智能技术的不断发展&#xff0c;自然语言处理技术也逐渐成为了研究的热点之一。其中&#xff0c;ChatGPT作为一项领先的自然语言处理技术…...

【Matlab算法】粒子群算法求解一维非线性函数问题(附MATLAB代码)

MATLAB求解一维非线性函数问题前言正文函数实现&#xff08;可视化处理&#xff09;可视化结果前言 一维非线性函数是指函数的自变量和因变量都是一维实数&#xff0c;而且函数的形式是非线性的&#xff0c;也就是不符合线性函数的形式。在一维非线性函数中&#xff0c;自变量…...

2023 最新发布超全的 Java 面试八股文,整整 1000道面试题,太全了

作为一名优秀的程序员&#xff0c;技术面试都是不可避免的一个环节&#xff0c;一般技术面试官都会通过自己的方式去考察程序员的技术功底与基础理论知识。 2023 年的互联网行业竞争越来越严峻&#xff0c;面试也是越来越难&#xff0c;很多粉丝朋友私信希望我出一篇面试专题或…...

产品经理面经|当面试官问你还有什么问题?

相信很多产品经理在跳槽面试的时候&#xff0c;在面试尾声都会遇到这样的环节&#xff0c;面试官会问你有什么问题要问的&#xff0c;一般来说大家都能随时随地甩出几个问题来化解&#xff0c;但其实在这个环节对于应聘者来说也是一个很好的机会来展现自己的能力&#xff0c;甚…...

单链表的基本操作

目录 一.链表的基本概念和结构 二.链表的分类 三.单链表的基本操作 1.创建一个节点 2.打印 3.尾插 4.头插 5.尾删 6.头删 7.查找 8.指定位置插入 9.指定位置删除 10.销毁 一.链表的基本概念和结构 概念&#xff1a;链表是一种物理存储结构上非连续、非顺序的存储结…...

【微信小程序-原生开发】系列教程目录(已完结)

01-注册登录账号&#xff0c;获取 AppID、下载安装开发工具、创建项目、上传体验 https://sunshinehu.blog.csdn.net/article/details/128663679 02-添加全局页面配置、页面、底部导航 https://sunshinehu.blog.csdn.net/article/details/128705866 03-自定义底部导航&#x…...

JavaEE--Thread 类的基本用法(不看你会后悔的嘿嘿)

Thread类是JVM用来管理线程的一个类,换句话说,每个线程都唯一对应着一个Thread对象. 因此,认识和掌握Thread类弥足重要. 本文将从 线程创建线程中断线程等待线程休眠获取线程实例 等方面来进行具体说明. 1)线程创建 方法1:通过创建Thread类的子类并重写run () 方法 class M…...

MySQL数据库基本使用(二)-------数据库及表的增删改查及字符集修改

1.MySQL数据库的使用演示 1.1创建自己的数据库 命令格式如下&#xff08;创建的数据库名称不能与已经存在的数据库重名&#xff09;&#xff1a; mysql> create database 数据库名;例如&#xff1a; mysql> create database atguigudb; #创建atguigudb数据库&#xf…...

互联网摸鱼日报(2023-03-17)

互联网摸鱼日报&#xff08;2023-03-17&#xff09; InfoQ 热门话题 开源新生代的成长之路&#xff1a;从校园到开源&#xff0c;需要迈过哪些挑战&#xff1f; 从 Clickhouse 到 Apache Doris&#xff0c;慧策电商 SaaS 高并发数据服务的改造实践 刚刚&#xff0c;百度文心…...

【前后端】低代码平台Jeecg-Boot 3.2宝塔云服务器部署流程

1 后端 部署流程 修改配置文件 更改数据库、redis的配置。 在system子模块中的target文件夹下生成 jar 包jeecg-boot-module-system-3.2.0.jar。 复制到云服务器 生成数据库 在这里插入图片描述 使用命令运行后端程序 java -jar ./jeecg-boot-module-system-3.2.0.jar宝…...

leetcode todolist

数组 数组的改变、移动 453. 最小移动次数使数组元素相等 665. 非递减数列 283. 移动零 数组的旋转 189. 旋转数组 396. 旋转函数 统计数组中的元素 645. 错误的集合 697. 数组的度 448. 找到所有数组中消失的数字 442. 数组中重复的数据 41. 缺失的第一个正数 数…...

改进YOLO系列 | CVPR2023最新 PConv | 提供 YOLOv5 / YOLOv7 / YOLOv7-tiny 模型 YAML 文件

DWConv是Conv的一种流行变体,已被广泛用作许多神经网络的关键构建块。对于输入 I ∈ R c h w I \in R^{c \times h \times w} I∈...

像ChatGPT玩转Excel数据

1.引言 最近ChatGPT的出现&#xff0c;把人工智能又带起了一波浪潮。机器人能否替代人类又成了最近热门的话题。 今天我们推荐的一个玩法和ChatGPT有点不一样。我们的课题是“让用户可以使用自然语言从Excel查询到自己想要的数据”。 要让自然语言可以从Excel中查数据&#…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

docker 部署发现spring.profiles.active 问题

报错&#xff1a; org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

GitHub 趋势日报 (2025年06月06日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...