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

关于redis锁的简单实现

新建RedisLock类。

public class RedisLock implements Serializable {
    private static final long serialVersionUID = 3077854413624876404L;

    private static final Log LOG = LogFactory.get(RedisLock.class);

    /**
     * 锁标志对应的key
     */
    private String lockKey;

    /**
     * 将key 的值设为value ,当且仅当key 不存在,等效于 SETNX。
     */
    private static final String NX = "NX";

    /**
     * seconds — 以秒为单位设置 key 的过期时间,等效于EXPIRE key seconds
     */
    private static final String EX = "EX";

    /**
     * 调用set后的返回值
     */
    private static final String OK = "OK";

    /**
     * 取锁等待中线程等待时间
     */
    private static final Long WAITING_SECONT = 200L;

    /**
     * 默认请求锁的等等时间,分钟
     */
    private static final Integer WAIT_MINUTE = 1;

    /**
     * 默认锁的有效时间
     */
    private static final Long EXPIRE = 5L;

    /**
     * 设置默认上锁时间单位
     */
    private static final TimeUnit TIME_UNIT = TimeUnit.MINUTES;

    /**
     * 解锁的lua脚本
     */
    private static final String UNLOCK_LUA;

    /**
     * 锁标记
     */
    private volatile boolean locked = false;

    static {
        StringBuilder sb = new StringBuilder();
        sb.append("if redislock.call(\"get\",KEYS[1]) == ARGV[1] ");
        sb.append("then ");
        sb.append("    return redislock.call(\"del\",KEYS[1]) ");
        sb.append("else ");
        sb.append("    return 0 ");
        sb.append("end ");
        UNLOCK_LUA = sb.toString();
    }

    /**
     * 锁的有效时间(s)
     */
    private Long expire = EXPIRE;

    /**
     * 请求锁的超时时间(ms)
     */
    private Integer waitMinute = WAIT_MINUTE;

    private TimeUnit timeUnit = TIME_UNIT;

    /**
     * Instantiates a new Redis lock.
     *
     * @param lockKey the lock key
     */
    public RedisLock(String lockKey) {
        this.lockKey = lockKey;
    }

    /**
     * Instantiates a new Redis lock.
     *
     * @param lockKey the lock key
     * @param expire  the expire
     */
    public RedisLock(String lockKey, Long expire) {
        this.lockKey = lockKey;
        this.expire = expire;
    }

    /**
     * Instantiates a new Redis lock.
     *
     * @param lockKey  the lock key
     * @param expire   the expire
     * @param timeUnit the time unit
     */
    public RedisLock(String lockKey, Long expire, TimeUnit timeUnit) {
        this.lockKey = lockKey;
        this.expire = expire;
        this.timeUnit = timeUnit;
    }

    /**
     * Instantiates a new Redis lock.
     *
     * @param lockKey    the lock key
     * @param expire     the expire
     * @param timeUnit   the time unit
     * @param waitMinute the wait minute
     */
    public RedisLock(String lockKey, Long expire, TimeUnit timeUnit, Integer waitMinute) {
        this.lockKey = lockKey;
        this.expire = expire;
        this.timeUnit = timeUnit;
        this.waitMinute = waitMinute;
    }

    /**
     * Try lock boolean.
     *
     * @return the boolean
     */
    public boolean tryLock(){
        try {
            if (StringUtils.isBlank(lockKey)) {
                LOG.debug("tryLock:key is blank");
                return false;
            }
            String value = UUID.fastUUID().toString();
            StringRedisTemplate redisTemplate = SpringUtils.getBean(StringRedisTemplate.class);
            locked = redisTemplate.opsForValue().setIfAbsent(lockKey, value, expire, timeUnit);
            // 取锁成功,直接返回false
            if (locked) {
                LOG.debug("tryLock:取锁成功");
                return locked;
            } else {
                if (waitMinute == null) {
                    LOG.debug("tryLock: 等待加锁时间为null,则不再尝试去加锁,直接返回false");
                    return false;
                }
                if (waitMinute < 0) {
                    LOG.debug("tryLock: 等待加锁时间小于0,则不再尝试去加锁,直接返回false");
                    return false;
                }
                long loseTime = DateUtil.offsetMinute(new Date(), waitMinute).getTime();
                // 不断尝试获取锁成功返回
                while (System.currentTimeMillis() < loseTime) {
                    locked = redisTemplate.opsForValue().setIfAbsent(lockKey, value, expire, timeUnit);
                    // 加锁成功
                    if (locked) {
                        return locked;
                    }
                    try {
                        Thread.sleep(WAITING_SECONT);
                    } catch (InterruptedException e) {
                        LOG.error(e, "tryLock 等待加锁的时候异常");
                    }
                }
                return locked;
            }
        } catch (Exception e) {
            LOG.error(e, "tryLock error");
            return false;
        }
    }

    /**
     * Is locked boolean.
     *
     * @return the boolean
     */
    public boolean isLocked(){
        return locked;
    }

    /**
     * Unlock.
     */
    public void unlock(){
        StringRedisTemplate redisTemplate = SpringUtils.getBean(StringRedisTemplate.class);
        redisTemplate.delete(lockKey);
    }

redis锁 service接口类

/**
     * 根据key取锁
     * @param key 健值
     * @return RedisLock
     */
    RedisLock getLock(String key);

    /**
     * 根据key取锁
     * @param key 健值
     * @param expire 过期时间
     * @return RedisLock
     */
    RedisLock getLock(String key, Long expire);

    /**
     * 根据key取锁
     * @param key 健值
     * @param expire 过期时间
     * @param timeUnit 过期时间单位
     * @return RedisLock
     */
    RedisLock getLock(String key, Long expire, TimeUnit timeUnit);

    /**
     * 根据key取锁,可以设置等待时间和过期时间
     * @param key key
     * @param waitMinute 等待时间,单位是分钟,在这个时间内会多次尝试获取锁,超过这个时间还没获得锁,就返回false
     * @param expire 加锁过期时间,为null时候,默认为-1L
     * @param timeUnit 单位,为null默认为Minutes
     * @return boolean true 成功,false 失败
     */
    RedisLock getLock(String key, Long expire, TimeUnit timeUnit, Integer waitMinute);

    /**
     * 释放锁
     * @param redisLock 锁对象
     * @date 2020年9月29日14:55:54
     */
    void unlock(RedisLock redisLock);

redis锁 service实现类

@DubboService
public class RedisLockServiceImpl implements IRedisLockService {

    private static final Log LOG = LogFactory.get(RedisLockServiceImpl.class);

    @Override
    public RedisLock getLock(String key) {
        if (StringUtils.isBlank(key)) {
            LOG.error("getLock: key is null");
            throw new BusinessException("取RedisLock的key不能为空");
        }
        return new RedisLock(key);
    }

    @Override
    public RedisLock getLock(String key, Long expire) {
        if (expire == null) {
            LOG.debug("getLock: expire is null");
            return this.getLock(key);
        }
        return new RedisLock(key, expire);
    }

    @Override
    public RedisLock getLock(String key, Long expire, TimeUnit timeUnit) {
        if (timeUnit == null) {
            LOG.debug("getLock: timeUnit is null");
            return this.getLock(key, expire);
        }
        return new RedisLock(key, expire, timeUnit);
    }

    @Override
    public RedisLock getLock(String key, Long expire, TimeUnit timeUnit, Integer waitMinute) {
        if (waitMinute == null) {
            LOG.debug("getLock: waitMinute is null");
            return this.getLock(key, expire, timeUnit);
        }
        return new RedisLock(key, expire, timeUnit, waitMinute);
    }

    @Override
    public void unlock(RedisLock redisLock) {
        if (redisLock != null) {
            redisLock.unlock();
        }
    }

简单的运用

    @Override
    @Transactional
    public void updateUser(V3User user) {
        //新增用户后立马更新用户 可能出现并发情况 导致数据重复
        String lockKey = "employeeResume::" + user.getId();
        RedisLock lock = redisLockService.getLock(lockKey, 10L, TimeUnit.SECONDS, 1);
        boolean locked = lock.tryLock();
        // 加锁失败,说明已经有别的程序在执行下面的保存操作,直接返回成功
        if (!locked) {
            return;
        }
        try {
           具体实现内容
        } finally {
            if (lock != null && lock.isLocked()) {
                lock.unlock();
            }
        }
    }

http://www.kler.cn/a/447707.html

相关文章:

  • 【ETCD】【源码阅读】深入分析 storeTxnWrite.Put方法源码
  • 【网络安全】用 Frida 修改软件为你所用
  • 【活动邀请·深圳】深圳COC社区 深圳 AWS UG 2024 re:Invent re:Cap
  • Tekscan压力分布测量系统:电池安全与质量提升的保障
  • 机动车油耗计算API集成指南
  • C++ 面向对象编程:友元、
  • GTID下复制问题和解决
  • 分布式系统架构5:限流设计模式
  • Python趣味游戏---自己改成图片,跟着鼠标移动,一直克隆自己,0.3秒钟后消除克隆体
  • Java学习,数组中查找指定元素
  • 数据分析时的json to excel 转换的好用小工具
  • 计算机毕业设计原创定制(免费送源码):NodeJS+MVVM+MySQL 樱花在线视频网站
  • 「Mac畅玩鸿蒙与硬件46」UI互动应用篇23 - 自定义天气预报组件
  • ORB-SLAM2源码学习:Tracking.cc:Tracking::NeedNewKeyFrame是否需要插入关键帧
  • Android settings命令详解
  • 科研笔记:CCS CONCEPTS
  • Spark和Hive的区别
  • 服务器上加入SFTP------(小白篇 1)
  • Bayes-GRU-Attention的数据多特征分类预测Matlab实现
  • 方正畅享全媒体新闻采编系统 screen.do SQL注入漏洞复现(附脚本)
  • 基于xiaothink对Wanyv-50M模型进行c-eval评估
  • 2024年企业中生成式 AI 的现状报告
  • wget -q -O
  • 数据结构 C/C++(实验七:排序)
  • macos 隐藏、加密磁盘、文件
  • Electric Capital 2024 年开发者报告 | 洞见未来 Web3 生态发展方向