分布式锁

Redis实现分布式锁 1. 为什么需要分布式锁?我们都知道,以上情况,,此时就2. 单机锁 & 分布式锁的定义:3. 分布式锁应该具备哪些条件?4. 分布式锁都有哪些主流的实现方案?分布式锁的核心是实现多线程之间互斥,而满足这一点的方式有很多,常见的有三种:补充:Zookeeper实现分布式锁的思路实现分布式锁的核心思路5. 如何用 Redis 实现分布式锁的?Redis本身可以被多个客户端访问,正好就是一个共享存储系统,可以用来保存分布式锁,而且Redis的读写性能高,可以应对高并发的锁操作场景。 阅读详情

Redis

  • 通过setnx函数来进行原子性设置值(如果存在,设置失败)
  • 通过Watch、multi、exec、unwatch的命令组合来实现原子性和数据的准确性
  • 使用的spring boot中的 RedisTemplate

代码

package com.wyj.destributed.lock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.locks.ReentrantLock;

public class RedisDistributedLock implements DistributedLock {

    private static Logger logger = LoggerFactory.getLogger(RedisDistributedLock.class);

    // 默认过期时间(毫秒)
    private static final int DEFAULT_EXPIRE_MS = 60000;

    private RedisTemplate redisTemplate;

    // 锁的key值
    private String lockKey;

    // 锁的过期时间间隔
    private int expireMS;

    private ReentrantLock lock;

    // 锁定成功的值
    private String lockedSuccessValue;

    public RedisDistributedLock(RedisTemplate redisTemplate, String lockKey) {
        this(redisTemplate, lockKey, DEFAULT_EXPIRE_MS);
    }

    public RedisDistributedLock(RedisTemplate redisTemplate, String lockKey, int expireMS) {
        this.redisTemplate = redisTemplate;
        this.lockKey = lockKey;
        this.expireMS = expireMS;
        lock = new ReentrantLock();
    }

    @Override
    public void lock() {
        // 使用lock的原因:主要为了防止同一个进程间的冲突,减少访问redis的次数
        lock.lock();
        try {
            for (; ; ) {
                if (tryAcquire()) return;
            }
        } finally {
            lock.unlock();
        }
    }

    private boolean tryAcquire() {
        // 生成锁的value
        long expires = System.currentTimeMillis() + expireMS;
        String lockValue = generateLockKey(expires);

        // 原子操作,设置值,如果存在,则设置失败;否则,设置成功
        if (setNx(lockValue)) {
            handleLockSuccessful(lockValue);
            return true;
        }
        try {
            // 检测是否过期
            // 通过redis事务,来保证操作的原子性
            // 通过监控来保证数据的准确性(具体可以查阅 Redis事务以及WATCH)
            return (boolean) redisTemplate.execute(new SessionCallback<Boolean>() {
                @Override
                public Boolean execute(RedisOperations operations) throws DataAccessException {
                    // 开启监控
                    operations.watch(lockKey);
                    // 获取当前value
                    String currentValue = (String) operations.boundValueOps(lockKey).get();
                    // 解析得到过期时间
                    long currentExpires = getCurrentExpires(currentValue);
                    // 判断是否过期
                    if (currentValue != null && currentExpires < System.currentTimeMillis()) {
                        // 开启事务
                        operations.multi();
                        operations.boundValueOps(lockKey).getAndSet(lockValue);
                        // 执行
                        List<String> exec = operations.exec();
                        // 判断是否执行成功
                        if (exec != null && exec.size() > 0 && exec.get(0).equals(currentValue)) {
                            handleLockSuccessful(lockValue);
                            return true;
                        }
                    } else {
                        // 取消监控
                        operations.unwatch();
                    }
                    return false;
                }
            });
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public void unlock() {
        try {
            if (lockedSuccessValue != null) {
                // 通过redis事务,来保证操作的原子性
                // 通过监控来保证数据的准确性(具体可以查阅 Redis事务以及WATCH)
                redisTemplate.execute(new SessionCallback<Boolean>() {
                    @Override
                    public Boolean execute(RedisOperations operations) throws DataAccessException {
                        operations.watch(lockKey);
                        String currentValue = (String) operations.boundValueOps(lockKey).get();
                        if (lockedSuccessValue.equals(currentValue)) {
                            operations.multi();
                            operations.delete(lockKey);
                            operations.exec();
                        } else {
                            operations.unwatch();
                        }
                        return null;
                    }
                });
            }
        } catch (Exception e) {
            logger.error("{} unlock error!", lockKey, e);
        }
    }

    // 锁定成功
    private void handleLockSuccessful(String lockValue) {
        lockedSuccessValue = lockValue;
    }

    // 生成锁定的value
    private String generateLockKey(long expires) {
        // 使用UUID,主要用来防止多个进程的时间点相同,导致的问题
        return expires + ":" + UUID.randomUUID().toString();
    }

    // 根据锁定的value获取当前的过期时间
    private static long getCurrentExpires(String currentValue) {
        if (currentValue == null) {
            return 0;
        }
        int i = currentValue.indexOf(":");
        return Long.parseLong(currentValue.substring(0, i));
    }

    // 设置value
    private boolean setNx(final String value) {
        return redisTemplate.opsForValue().setIfAbsent(lockKey, value);
    }
}
Redis实战之 分布式锁 重入问题:重入问题是指 获得锁的线程可以再次进入到相同的锁的代码块中,可重入锁的意义在于防止死锁,比如HashTable这样的代码中,他的方法都是使用synchronized修饰的,假如他在一个方法内,调用另一个方法,那么此时如果是不可重入的,不就死锁了吗?所以可重入锁他的主要意义是防止死锁,我们的synchronized和Lock锁都是可重入的。不可重试:是指目前的分布式只能尝试一次,我们认为合理的情况是:当线程在获得锁失败后,他应该能再次尝试获得锁。 阅读详情

相关推荐

Redis的分布式锁详解

3)为了有效解决既保证锁完全有效性 和 性能高效问题:antirez又提出了“延迟重启”的概念,redis同步到磁盘方式保持默认的每秒1次,在redis崩溃单机后(无论是一个还是所有),先不立即重启它,而是等待TTL时间后再重启,这样的话,这个节点在重启前所参与的锁都会过期,它在重启后就不会对现有的锁造成影响,缺点是在TTL时间内服务相当于暂停状态;redisson在获取锁之后,会维护一个看门狗线程,在每一个锁设置的过期时间的1/3处,如果线程还没执行完任务,则不断延长锁的有效期。...

青藤的博客 5370

什么是分布式锁?几种分布式锁分别是怎么实现的?

一、什么是分布式锁: 1、什么是分布式锁: 分布式锁,即分布式系统中的锁。在单体应用中我们通过锁解决的是控制共享资源访问的问题,而分布式锁,就是解决了分布式系统中控制共享资源访问的问题。与单体应用不同的是,分布式系统中竞争共享资源的最小粒度从线程升级成了进程。 2、分布式锁应该具备哪些条件: 在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行 高可用的获取锁与释放锁 高性能的获取锁与释放锁 具备可重入特性(可理解为重新进入,由多于一个任务并发使用,而

weixin_74412978的博客 6294

什么是分布式锁?为什么要用分布式锁?

推荐阅读: 这套Github上40K+star学习笔记,可以帮你搞定95%以上的Java面试 毫不夸张的说,这份SpringBoot学习指南能解决你遇到的98%的问题 最全面试题新鲜出炉:70+算法题、近30种大厂面试笔试常考知识点 什么是分布式锁?在回答这个问题之前,我们先回答一下什么是锁。 普通的锁,即在单机多线程环境下,当多个线程需要访问同一个变量或代码片段时,被访问的变量或代码片段叫做临界区域,我们需要控制线程一个一个的顺序执行,否则会出现并发问题。 如何控制呢?就是设置一个各个线程

lonely_bin的博客 3403

分布式锁Redisson详解(重点)

一、为什么需要分布式锁? 在单体web应用的时候,如果多个线程要访问共享资源的时候,我们通常线程间加锁的机制,在某一个时刻,只有一个线程可以对这个资源进行操作,其他线程需要等待锁的释放,Java中也有一些处理锁的机制,比如synchronized。 而到了分布式的环境中,当某个资源可以被多个系统访问使用到的时候,为了保证大家访问这个数据是一致性的,那么就要求再同一个时刻,只能被一个系统使用,这时候线程之间的锁机制就无法起到作用了,因为分布式环境中,系统是会部署到不同的机器上面的,那么就需要【

Java后端技术栈 8404

【五斗米】实现分布式锁的三种方式

在单击环境下,就是只有当前JVM虚拟机A中的多个线程对同一个Object(共享资源)进行操作,可以采用同步互斥(就是只有同一时间只有一个线程对共享资源进行事务操作)的方式,我们常用的方式就是采用加锁的方式解决,虽然不能保证数据的强一致性,但是能够保证数据的最终一致性。........................

m0_64210833的博客 9576

C# Log4Net不输出日志的解决方法

log4net是一个流行的 .NET 日志框架,用于记录应用程序的日志信息。如果你在使用log4net。

精通搬砖,资深技术砖家,在软件开发,人工智能,设备开发等领域都有研究。商业合作&交流学习可私信联系。 1263

【分布式】分布式锁

在 Java 中,我们通常使用 ReetrantLock 类、synchronized 关键字这类 本地锁 来控制一个 JVM 进程内的多个线程对本地共享资源的访问不同的服务/客户端通常运行在独立的 JVM 进程上。如果多个 JVM 进程共享同一份资源的话,使用本地锁就没办法实现资源的互斥访问了。于是,分布式锁就诞生了。举个例子:系统的订单服务一共部署了 3 份,都对外提供服务。用户下订单之前需要检查库存,为了防止超卖,这里需要加锁以实现对检查库存操作的同步访问。

kazuhura的博客 3622

图形学基础|球谐光照(Spherical Harmonics Lighting)

球谐光照(Spherical Harmonics Lighting) 文章目录球谐光照(Spherical Harmonics Lighting)一、前言二、球谐函数2.1 基函数2.2 投影与重建2.3 应用三、漫反射环境光3.1 IrradianceMap3.2 数学推导3.3 实践参考博文 一、前言 在学习图形渲染的过程中,一直對球谐函数(球谐光照)有一点了解,但没有亲手实现过。 关于球谐函数的数学概念是比较复杂的,笔者也实在无法完全理解,这篇文章只能从做东西的角度来说,公式是如何和代码进行对应的。

【黑键】 1万+

关于分布式锁的面试题都在这里了|Reids分布式锁|ZooKeeper分布式锁

我今天班儿都没上,就为了赶紧把这篇文章分布式锁早点写完。我真的不能再贴心了。 边喝茶边构思,你们可不要白嫖了! 三连来一遍? 引言 为什么要学习分布式锁? 最简单的理由就是作为一个社招程序员,面试的时候一定被面啦,你看怎么多公众号都翻来覆去的发分布式锁的主题,可见它很重要啦,在高考里这就是送分题,不要怪可惜的。 那应届生也会问吗?这就不一定了,但是,如果你会,面试官肯定会多给你那点分(钱) 第三...

6019

环境配置 |Jupyter lab/Jupyter Notebook 安装与设置

ipynb使用Jupyterlab/Jupyter Notebook 来编写程序时的文件,在使用时,可以现转换为标准的.py的python文件。

weixin_44649780的博客 5771

玄机-Linux权限维持-后门

玄机-Linux权限维持-后门

m0_46439803的博客 764

Redis的分布式锁分析

开发同学都知道,在分布式系统中,为了保证多个机器并发操作的数据一致性,常常需要使用分布式锁机制。常用的比如数据库锁,ZK锁,Reis锁等等,今天我们就探讨下Redis分布式锁,说说他的实现原理和应用场景,并介绍如何正确使用分布式锁来解决并发问题

战斧的博客 7280

【QMIX】一种基于Value-Based多智能体算法

文章目录1. QMIX 解决了什么问题(Motivation)2. QMIX 怎样解决团队收益最大化问题(Method)2.1 算法大框架 —— 基于 AC 框架的 CTDE(Centralized Training Distributed Execution) 模式 QMIX 是一种基于 Value-Based 的多智能体强化学习算法(MARL),其基本思想来源于 Actor-Critic 与 DQN 的结合。使用中心式学习(Centralized Learning)分布式执行(Distributed

qq_38638132的博客 9860

三、详解Redis分布式锁&Redisson分布式锁

在传统的java进程中,我们常常用Synchronized或者ReentrantLock来对临界区进行加锁,防止多个线程之间并行访问,导致数据读写异常。但是这种锁的粒度仅限于当前jvm中,在工业生产环境下,往往一个web项目会部署多台机器,也就意味着会有多个jvm。那么这几个jvm是独立的,这就导致上述的锁失效。分布式锁是一种在分布式系统环境下,通过多个节点对共享资源进行访问控制的机制。

2301_77203215的博客 2534

深入解析.Net中SM2与sm-crypto的跨平台加解密实践

本文深入探讨了在.Net平台与前端sm-crypto库之间实现SM2国密算法跨平台加解密的实践方案。针对对接中常见的密钥格式、密文拼接顺序(C1C3C2与C1C2C3)等核心难点,提供了基于BouncyCastle的完整工具类实现、前后端联调示例及问题排查指南,旨在帮助开发者构建安全、兼容的数据加密通道。

weixin_29322855的博客 369

Android 14 (AOSP) 源码架构深度解析与开发者指南

Android14源码的仓库指南信息

保持专注,保持热爱 1186

什么时候需要分布式锁?

比如: 高并发下争夺共享资源,比如秒杀对于库存这种共享资源需要用到分布式锁,如果不用分布式锁很可能造成超卖。 分布式锁也是锁 在单体应用的时候,如果多个线程要访问共享资源的时候,我们通常线程间加锁的机制,在某一个时刻,只有一个线程可以对这个资源进行操作,其他线程需要等待锁的释放,Java中也有一些处理锁的机制,比如synchronized。 而到了分布式的环境中,当某个资源可以...

La victoria的博客 1万+

OpenClaw界面操作手册:命令行与交互模式的正确打开方式

"test": {"description": "运行项目的测试套件","prompt": "请执行项目的测试命令,并分析测试结果。如果有失败的测试,请提供修复建议。},"lint": {"description": "运行代码检查","prompt": "请运行代码检查工具,分析所有警告和错误,并提供修复建议。},"doc": {"description": "为当前文件生成文档","prompt": "请为当前打开的文件生成完整的API文档,包括函数说明、参数描述、返回值类型和使用示例。

gaoxiang_120的专栏 3147

分布式锁详解

它管理数据一致性,防止多个节点同时修改相同数据,处理资源竞争,保障事务原子性,避免任务重复执行,同时协调和同步节点操作,减少死锁的可能性。例如,在表中记录锁的创建时间,并定期检查是否超时,如果超时则自动释放锁。只有在比自己序号小的节点被删除后,才会再次检查自己是否成为最小的节点,进而获取锁。利用顺序节点实现锁的公平性,保证了锁的获取顺序。,查出来的数据是相同的,然后依次执行库存减一操作,此时库存会变成-1件,这就造成了超卖问题。当释放锁时,节点会检查锁的持有者是否匹配,只有匹配的情况下才会删除锁。

努力分享一些干货 2609

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Redis的分布式锁

Redis分布式锁通过Redis原子操作实现互斥访问,核心是SETNX命令设置带过期时间的键值对,配合Lua脚本确保安全释放。Java实现可通过Jedis手动处理锁获取、唯一标识验证和释放逻辑,或使用Redisson简化开发,其提供自动续期、可重入锁等功能。Redlock算法支持多节点部署提高可靠性。注意事项包括合理设置锁超时、确保原子操作、处理时钟漂移等。适用于秒杀、任务调度等需要互斥访问的场景,Jedis适合简单需求,Redisson更适合复杂场景,能显著降低开发复杂度。

Hzx895554135的博客 2702

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

分布式锁、ZK分布式锁、Redis分布式锁

深入刨析zookeeper分布式锁,redis分布式锁的实现原理。两者之间的对比。

weixin_35794878的博客 1945

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

分布式锁【Redis场景分布式锁篇】

分布式锁,Redis锁,Redis

席万里的博客 2439

什么是分布式锁,分布式锁的几种实现方式

不具备可重入的特性,因为同一个线程在释放锁之前,行数据一直存在,无法再次成功插入数据,所以,需要在表中新增一列,用于记录当前获取到锁的机器和线程信息,在再次获取锁的时候,先查询表中机器和线程信息是否和当前机器和线程相同,若相同则直接获取锁;没有锁失效机制,因为有可能出现成功插入数据后,服务器宕机了,对应的数据没有被删除,当服务恢复后一直获取不到锁,所以,需要在表中新增一列,用于记录失效时间,并且需要有定时任务清除这些失效的数据;在这个例子中,锁用于控制访问数据库的流量,最终起到了保护系统的作用。

w_l666的博客 2070
上一篇: 偏差
下一篇: Java 获取本机IP
嘿嘿不错
博客等级 码龄11年 10粉丝 128原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值