分布式锁的应用

2023-10-26

package com.itheima.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.concurrent.TimeUnit;

public class RedisLock {

    private static Logger log = LoggerFactory.getLogger(RedisLock.class);
    private StringRedisTemplate stringRedisTemplate;
    private String lockKey;
    private String value;
    private static final String PREFIX_KEY = "gb.common.distributed.lock.redis_";
    private boolean locked;
    private int expireSecond;
    private int timeout;
    private static final int DEFAULT_RETRY_INTERVAL_MILLIS = 100;
    private int retryCount;
    private int retryIntervalMillisecond;

    /**
     * @deprecated
     */
    @Deprecated
    public RedisLock(StringRedisTemplate stringRedisTemplate, String lockKey) {
        this.locked = false;
        this.expireSecond = 60;
        this.timeout = 0;
        this.retryCount = 0;
        this.retryIntervalMillisecond = 0;
        this.stringRedisTemplate = stringRedisTemplate;
        this.lockKey = "gb.common.distributed.lock.redis_" + lockKey;
    }

    public RedisLock(StringRedisTemplate stringRedisTemplate, String lockKey, int expireSecond) {
        this.locked = false;
        this.expireSecond = 60;
        this.timeout = 0;
        this.retryCount = 0;
        this.retryIntervalMillisecond = 0;
        this.stringRedisTemplate = stringRedisTemplate;
        this.lockKey = "gb.common.distributed.lock.redis_" + lockKey;
        this.expireSecond = expireSecond;
    }

    public RedisLock(StringRedisTemplate stringRedisTemplate, String lockKey, int expireSecond, int timeout) {
        this(stringRedisTemplate, lockKey, expireSecond);
        this.timeout = timeout;
    }

    public RedisLock(StringRedisTemplate stringRedisTemplate, String lockKey, int expireSecond, int retryCount, int retryIntervalMillisecond) {
        this(stringRedisTemplate, lockKey, expireSecond);
        this.retryCount = retryCount;
        this.retryIntervalMillisecond = retryIntervalMillisecond;
    }

    /**
     * @deprecated
     */
    @Deprecated
    public boolean acquireLock(long expireSecond) {
        if (expireSecond <= 0L) {
            throw new RuntimeException("获取redids分布锁,过期秒数应大于零!");
        } else {
            this.expireSecond = (int) expireSecond;
            return this.acquireLock();
        }
    }

    /**
     * @deprecated
     */
    @Deprecated
    public boolean acquireLock() {
        return this.lock();
    }

    public boolean lock() {
        log.debug("begin redisLock lock");

        // value = 时间戳(redis客户端) + 过期时间(毫秒) + 1L
        long lockTimeout = this.getCurrentTimeMillisFromRedis() + (long) (this.expireSecond * 1000) + 1L;
        // 将value转成String类型
        String strLockTimeout = String.valueOf(lockTimeout);

        /**
         * 上锁, 调用redis原生的setNX方法
         *  -如果返回true, 表示Redis中没有这个key, 上锁成功
         *  -如果返回false, 表示Redis中已经存在这个key, 抢锁失败
         */
        if (this.setNX(this.lockKey, strLockTimeout, (long) this.expireSecond)) {
            // 设置上锁状态为true
            this.locked = true;
            this.value = strLockTimeout;
            log.debug("setNX成功");
            log.debug("end redisLock lock");
            return true;
        } else {
            // 抢锁失败, 先拿到redis中当前key对应的value, 这个value代表当前锁的失效时间
            String strCurrentLockTimeout = (String) this.stringRedisTemplate.opsForValue().get(this.lockKey);
            log.debug("lockKey:{},strCurrentLockTimeout:{}", this.lockKey, strCurrentLockTimeout);

            // 判断锁是否失效了
            if (strCurrentLockTimeout != null && Long.parseLong(strCurrentLockTimeout) < this.getCurrentTimeMillisFromRedis()) {
                log.debug("锁已过期!");

                // getAndSet:获取原来key键对应的值并重新赋新值。
                String strOldLockTimeout = (String) this.stringRedisTemplate.opsForValue().getAndSet(this.lockKey, strLockTimeout);

                //
                if (strOldLockTimeout != null && strOldLockTimeout.equals(strCurrentLockTimeout)) {
                    log.debug("重新抢到锁");
                    this.stringRedisTemplate.expire(this.lockKey, (long) (this.expireSecond * 1000), TimeUnit.MILLISECONDS);
                    this.value = strLockTimeout;
                    this.locked = true;
                    log.debug("end redisLock lock");
                    return true;
                }
            }

            log.debug("未抢到锁");
            log.debug("end redisLock lock");
            return false;
        }
    }

    public boolean tryLock() {
        if (this.retryCount > 0 && this.retryIntervalMillisecond > 0) {
            do {
                if (this.lock()) {
                    return true;
                }

                try {
                    Thread.sleep((long) this.retryIntervalMillisecond);
                } catch (InterruptedException var2) {
                    var2.printStackTrace();
                }

                --this.retryCount;
            } while (this.retryCount > 0);
        } else {
            do {
                if (this.lock()) {
                    return true;
                }

                this.timeout -= 100;
            } while (this.timeout > 0);
        }

        return false;
    }

    /**
     * @deprecated
     */
    @Deprecated
    public void releaseLock() {
        this.unlock();
    }

    public void unlock() {
        log.debug("begin redisLock unlock");
        if (this.locked) {
            String strCurrentLockTimeout = (String) this.stringRedisTemplate.opsForValue().get(this.lockKey);
            if (strCurrentLockTimeout == null) {
                log.debug("锁已不存在了");
            } else {
                this.stringRedisTemplate.delete(this.lockKey);
            }

            this.locked = false;
        } else {
            log.debug("原来就没锁住");
        }

        log.debug("end redisLock unlock");
    }

    private boolean setNX(final String key, final String value, final long second) {
        return (Boolean) this.stringRedisTemplate.execute(new RedisCallback<Boolean>() {
            public Boolean doInRedis(RedisConnection connection) {
                byte[] keyBytes = RedisLock.this.stringRedisTemplate.getStringSerializer().serialize(key);
                boolean locked = connection.setNX(keyBytes, RedisLock.this.stringRedisTemplate.getStringSerializer().serialize(value));
                if (locked && second > 0L) {
                    connection.expire(keyBytes, second);
                }

                return locked;
            }
        });
    }

    /**
     * 获取redis客户端的系统时间(毫秒)
     */
    public long getCurrentTimeMillisFromRedis() {
        return (Long) this.stringRedisTemplate.execute(new RedisCallback<Long>() {
            public Long doInRedis(RedisConnection redisConnection) throws DataAccessException {
                return redisConnection.time();
            }
        });
    }

    public String getLockKey() {
        return this.lockKey;
    }

    public boolean islocked() {
        return this.locked;
    }


}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

分布式锁的应用 的相关文章

随机推荐

  • 小学使用计算机类型的题,湖南省2008年对口招生考试计算机应用类综合试题卷.doc...

    湖南省2008年对口招生考试计算机应用类综合试题卷 湖南省2008年普通高等学校对口招生考试 计算机应用类专业综合知识试卷 时量150分钟满分300分 单选题 本大题共10个小题 每小题3分 共30分 1 计算机的运算器与控制器的总称是 A
  • 高德离线地图vue-amap的api文档(2):创建地图,撒点等等

    前言 高德离线地图的使用场景还是很多的 但是他的api在国外 想参考api对网络差的朋友来说不是一件容易的事 对我来说一样 在这里整理下他的api内容 注 本文是将官网api挪动出来 网好的童鞋想看原版的请点击官网入口 目录 十七 搜索框
  • UE4 实现拖尾和车辆轨迹效果

    本文主要讲解如何实现类似车辆拖尾以及车辆轨迹的效果 1 创建蓝图BP Spline 2 创建材质和两个材质实例 3 创建蓝图BP Car Timeline设置 SplineMesh设置 4 将BP Car丢到世界场景内进行测试 并在关卡蓝图
  • 公司使用了 6 年的Spring Boot 项目部署方案,打包 + Shell 脚本部署详解,稳的一批

    本篇和大家分享的是 Spring Boot 打包并结合 Shell 脚本命令部署 重点在分享一个shell 程序启动工具 希望能便利工作 profiles指定不同环境的配置 maven assembly plugin打发布压缩包 分享she
  • 三层交换机静态路由配置实现两网互通

    一 目标 两个交换机上属于不同网络 现通过静态路由配置 实现两网互通 二 网络示意图 三 配置流程 1 对两个交换机进行vlan划分 分别加入vlan100 2 分别将两交换机的1 4接口划分到vlan100 3 分别给两交换机的4个vla
  • mingw离线安装以及配置pacman源

    前提 win10 64 vs至少16 由于网络原因 mingw无法下载国内或者国外任何源 显示timeout 在经过一番倒腾之后 mingw工具本质可以当作一个虚拟的archlinux环境用 从官网下载mingw http www msys
  • SQL注入之联合查询和报错注入

    联合查询 联合查询就是利用SQL 语句union select union select 会把两条SQL 语句的查询结果拼接起来 形成一张虚拟的表 联合查询会实现跨库跨表查询 会极大减少SQL 注入的成本 联合查询有两个限制条件 两条SQL
  • 云媒易:提高小红书推广效果的3大核心玩法

    做小红书的产品投放 相信大家考虑的都是用更低的成本去取得最大的效果 那么我们在小红书做推广时 应该怎么掌握投放效果呢 想要掌握小红书的投放效果 掌握它的核心玩法是关键 小红书推广投放三大核心 KOL 内容 节奏 一 KOL 很多人觉得 种草
  • 【golang/go语言】Go语言之反射

    本文参考了李文周的博客 Go语言基础之反射 一 反射初识 1 什么是反射 在计算机科学中 反射是指计算机程序在运行时 run time 可以访问 检测和修改它本身状态和行为的一种能力 用比喻来说 反射就是程序在运行的时候能够观察并修改自己的
  • 修改apache服务器默认的端口号,关于apache:Apache修改默认端口

    Apache批改默认端口 1 批改配置文件 1 批改 etc apache2 ports conf 将 NameVirtualHost 80 Listen 80 改为本人须要的端口 NameVirtualHost 9000 Listen 9
  • 基于深度学习的岩石样本智能识别研究——第九届“泰迪杯”挑战赛B题优秀作品

    1 前言 1 1 研究背景 岩石是现代建筑业和制造业的重要原材料 除了作为原材料使用以外 还可以对其进行勘探开发挖掘岩油气藏 若能智能且准确地识别岩石岩性 计算岩石含油量 这将会是一笔巨大的社会财富 天然岩石有多种来源和用途 根据其不同的特
  • keil安装GD32 pack包安装不上 不显示 没有了

    今天移植STM32F4的代码到GD32F4上 发现pack包安装不上去 下图列表中 找不到GigaDevice 而是一堆其它厂家芯片 解决方法 找到GD32F4xx AddOn V2 2 0 rar 安装其中的文件即可 可根据GD型号自行下
  • 信号完整性分析基础知识之传输线和反射(七):带负载传输线、感性不连续引起的反射

    带负载传输线 如果在传输线上有一个小的容性负载 信号会出现失真 上升时间也会降低 每个分立电容都会降低信号在其附近看到的阻抗 如果传输线上分布有多个容性负载 例如一个总线上每隔1 2inch有一个2pF的连接器残桩 或者一个内存总线上每隔0
  • 08Nginx源码分析之单向链表结构(ngx_list.c)

    一 单向链表结构 Nginx的list单向链表的结构和Nginx的数组结构Array有点类似 总体来说 数据结构也是非常简单清晰的 1 ngx list t 链表结构 ngx list t是管理链表的结构 包含以下成员 链表结构 typed
  • 创建型模式1——单例模式

    学习完C 的静态成员的相关知识 我们先来了解设计模式中最简单的一种 单例模式 单例模式的动机 对于一个软件系统的某些类而言 我们没有必要创建多个实例化对象 就比如Windows系统的任务管理器或回收站 我们无论点击多少次都只会弹出一个窗口
  • Linux服务使用宝塔面板搭建网站,并发布公网访问

    文章目录 前言 1 环境安装 2 安装cpolar内网穿透 3 内网穿透 4 固定http地址 5 配置二级子域名 6 创建一个测试页面 前言 宝塔面板作为简单好用的服务器运维管理面板 它支持Linux Windows系统 我们可用它来一键
  • matlab的零极点分布图,matlab零极点分布图

    Matlab中绘制零极点 数学 自然科学 专业资料 ZPLANE Z plane 由连续时间系统和 离散时间系统系统函数求频率响应 绘制零极点图并使用零极点 图判断系统稳定性 感受到了使用 matlab 分析这些内容的简便 进一步掌握了 m
  • C++ 多线程

    多线程是多任务处理的一种特殊形式 多任务处理允许让电脑同时运行两个或两个以上的程序 一般情况下两种类型的多任务处理 基于进程和基于线程 基于进程的多任务处理是程序的并发执行 基于线程的多任务处理是同一程序的片段的并发执行 多线程程序包含可以
  • 组合问题1

    给出1到n的n个数 返回k个数组合 输入n 4 k 2 输出 1 2 1 3 1 4 2 3 2 4 3 4 include
  • 分布式锁的应用

    package com itheima utils import org slf4j Logger import org slf4j LoggerFactory import org springframework dao DataAcce