0

0

redis分布式锁的实现原理实例分析

WBOY

WBOY

发布时间:2023-05-26 16:19:14

|

1269人浏览过

|

来源于亿速云

转载

首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:

1.互斥性。在任意时刻,只有一个客户端能持有锁。

2.不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。

3.具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。

4.解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。

下边是代码实现,首先我们要通过Maven引入Jedis开源组件,在pom.xml文件加入下面的代码:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.1.0</version>
</dependency>

分布式锁实现代码,DistributedLock.java

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.exceptions.JedisException;
import java.util.List;
import java.util.UUID;
/**
 * @author swadian
 * @date 2022/3/4
 * @Version 1.0
 * @describetion Redis分布式锁原理
 */
public class DistributedLock {
    //redis连接池
    private static JedisPool jedisPool;
    static {
        JedisPoolConfig config = new JedisPoolConfig();
        // 设置最大连接数
        config.setMaxTotal(200);
        // 设置最大空闲数
        config.setMaxIdle(8);
        // 设置最大等待时间
        config.setMaxWaitMillis(1000 * 100);
        // 在borrow一个jedis实例时,是否需要验证,若为true,则所有jedis实例均是可用的
        config.setTestOnBorrow(true);
        jedisPool = new JedisPool(config, "192.168.3.27", 6379, 3000);
    }
    /**
     * 加锁
     * @param lockName       锁的key
     * @param acquireTimeout 获取锁的超时时间
     * @param timeout        锁的超时时间
     * @return 锁标识
     * Redis Setnx(SET if Not eXists) 命令在指定的 key 不存在时,为 key 设置指定的值。
     * 设置成功,返回 1 。 设置失败,返回 0 。
     */
    public String lockWithTimeout(String lockName, long acquireTimeout, long timeout) {
        Jedis jedis = null;
        String retIdentifier = null;
        try {
            // 获取连接
            jedis = jedisPool.getResource();
            // value值->随机生成一个String
            String identifier = UUID.randomUUID().toString();
            // key值->即锁名
            String lockKey = "lock:" + lockName;
            // 超时时间->上锁后超过此时间则自动释放锁 毫秒转成->秒
            int lockExpire = (int) (timeout / 1000);
            // 获取锁的超时时间->超过这个时间则放弃获取锁
            long end = System.currentTimeMillis() + acquireTimeout;
            while (System.currentTimeMillis() < end) { //在获取锁时间内
                if (jedis.setnx(lockKey, identifier) == 1) {//关键:设置锁
                    jedis.expire(lockKey, lockExpire);
                    // 返回value值,用于释放锁时间确认
                    retIdentifier = identifier;
                    return retIdentifier;
                }
                // ttl以秒为单位返回 key 的剩余过期时间,返回-1代表key没有设置超时时间,为key设置一个超时时间
                if (jedis.ttl(lockKey) == -1) {
                    jedis.expire(lockKey, lockExpire);
                }
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        } catch (JedisException e) {
            e.printStackTrace();
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return retIdentifier;
    }
    /**
     * 释放锁
     * @param lockName   锁的key
     * @param identifier 释放锁的标识
     * @return
     */
    public boolean releaseLock(String lockName, String identifier) {
        Jedis jedis = null;
        String lockKey = "lock:" + lockName;
        boolean retFlag = false;
        try {
            jedis = jedisPool.getResource();
            while (true) {
                // 监视lock,准备开始redis事务
                jedis.watch(lockKey);
                // 通过前面返回的value值判断是不是该锁,若是该锁,则删除,释放锁
                if (identifier.equals(jedis.get(lockKey))) {
                    Transaction transaction = jedis.multi();//开启redis事务
                    transaction.del(lockKey);
                    List<Object> results = transaction.exec();//提交redis事务
                    if (results == null) {//提交失败
                        continue;//继续循环
                    }
                    retFlag = true;//提交成功
                }
                jedis.unwatch();//解除监控
                break;
            }
        } catch (JedisException e) {
            e.printStackTrace();
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return retFlag;
    }
}

为了验证它,我们创建SkillService.java业务类

华友协同办公自动化OA系统
华友协同办公自动化OA系统

华友协同办公管理系统(华友OA),基于微软最新的.net 2.0平台和SQL Server数据库,集成强大的Ajax技术,采用多层分布式架构,实现统一办公平台,功能强大、价格便宜,是适用于企事业单位的通用型网络协同办公系统。 系统秉承协同办公的思想,集成即时通讯、日记管理、通知管理、邮件管理、新闻、考勤管理、短信管理、个人文件柜、日程安排、工作计划、工作日清、通讯录、公文流转、论坛、在线调查、

下载
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SkillService {
    final DistributedLock lock = new DistributedLock();
    public static final String LOCK_KEY = "lock_resource";
    int n = 500;
    /**
     * 线程业务方法
     */
    public void seckill() {
        // 返回锁的value值,供释放锁时候进行判断
        String identifier = lock.lockWithTimeout(LOCK_KEY, 5000, 1000);
        log.info("线程:"+Thread.currentThread().getName() + "获得了锁");
        log.info("剩余数量:{}",--n);
        lock.releaseLock(LOCK_KEY, identifier);
    }
}

如果找不到@Slf4j日志,在pom.xml文件加入下面的代码:

<!--@Slf4j日志依赖组件-->
  <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
</dependency>

编辑一个测试类TestLock.java

/**
 * @author swadian
 * @date 2022/3/4
 * @Version 1.0
 */
public class TestLock {
    public static void main(String[] args) {
        SkillService service = new SkillService();
        for (int i = 10; i < 60; i++) { //开50个线程
            SkillThread skillThread = new SkillThread(service, "skillThread->" + i);
            skillThread.start();
        }
    }
}
class SkillThread extends Thread {
    private SkillService skillService;
    public SkillThread(SkillService skillService, String skillThreadName) {
        super(skillThreadName);
        this.skillService = skillService;
    }
    @Override
    public void run() {
        skillService.seckill();
    }
}

测试结果显示,加锁后剩余数量全部是顺序串行的,499,498,497...

redis分布式锁的实现原理实例分析

 我们修改SkillService.java业务类,注释掉加锁逻辑

@Slf4j
public class SkillService {
    final DistributedLock lock = new DistributedLock();
    public static final String LOCK_KEY = "lock_resource";
    int n = 500;
    /**
     * 线程业务方法
     */
    public void seckill() {
        // 返回锁的value值,供释放锁时候进行判断
        //String identifier = lock.lockWithTimeout(LOCK_KEY, 5000, 1000);
        log.info("线程:"+Thread.currentThread().getName() + "获得了锁");
        log.info("剩余数量:{}",--n);
        //lock.releaseLock(LOCK_KEY, identifier);
    }
}

重新执行测试,注释掉加锁逻辑后,剩余数量全部是乱序的,472,454,452...

redis分布式锁的实现原理实例分析

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
什么是分布式
什么是分布式

分布式是一种计算和数据处理的方式,将计算任务或数据分散到多个计算机或节点中进行处理。本专题为大家提供分布式相关的文章、下载、课程内容,供大家免费下载体验。

406

2023.08.11

分布式和微服务的区别
分布式和微服务的区别

分布式和微服务的区别在定义和概念、设计思想、粒度和复杂性、服务边界和自治性、技术栈和部署方式等。本专题为大家提供分布式和微服务相关的文章、下载、课程内容,供大家免费下载体验。

251

2023.10.07

Java Maven专题
Java Maven专题

本专题聚焦 Java 主流构建工具 Maven 的学习与应用,系统讲解项目结构、依赖管理、插件使用、生命周期与多模块项目配置。通过企业管理系统、Web 应用与微服务项目实战,帮助学员全面掌握 Maven 在 Java 项目构建与团队协作中的核心技能。

0

2025.09.15

pdf怎么转换成xml格式
pdf怎么转换成xml格式

将 pdf 转换为 xml 的方法:1. 使用在线转换器;2. 使用桌面软件(如 adobe acrobat、itext);3. 使用命令行工具(如 pdftoxml)。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1946

2024.04.01

xml怎么变成word
xml怎么变成word

步骤:1. 导入 xml 文件;2. 选择 xml 结构;3. 映射 xml 元素到 word 元素;4. 生成 word 文档。提示:确保 xml 文件结构良好,并预览 word 文档以验证转换是否成功。想了解更多xml的相关内容,可以阅读本专题下面的文章。

2119

2024.08.01

xml是什么格式的文件
xml是什么格式的文件

xml是一种纯文本格式的文件。xml指的是可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。想了解更多相关的内容,可阅读本专题下面的相关文章。

1168

2024.11.28

常用的数据库软件
常用的数据库软件

常用的数据库软件有MySQL、Oracle、SQL Server、PostgreSQL、MongoDB、Redis、Cassandra、Hadoop、Spark和Amazon DynamoDB。更多关于数据库软件的内容详情请看本专题下面的文章。php中文网欢迎大家前来学习。

1006

2023.11.02

内存数据库有哪些
内存数据库有哪些

内存数据库有Redis、Memcached、Apache Ignite、VoltDB、TimesTen、H2 Database、Aerospike、Oracle TimesTen In-Memory Database、SAP HANA和ache Cassandra。更多关于内存数据库相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

671

2023.11.14

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

3

2026.03.11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
进程与SOCKET
进程与SOCKET

共6课时 | 0.4万人学习

Redis+MySQL数据库面试教程
Redis+MySQL数据库面试教程

共72课时 | 7.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号