关于redis锁的简单实现

新建RedisLock类。

bash 复制代码
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接口类

bash 复制代码
/**
     * 根据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实现类

bash 复制代码
@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();
        }
    }

简单的运用

bash 复制代码
    @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();
            }
        }
    }
相关推荐
Mr. zhihao1 分钟前
Spring Boot 条件注解:@ConditionalOnProperty 完全解析
java·spring boot·后端
追逐梦想永不停11 分钟前
Java连接chatGPT步骤(免费key获取方法)
java·开发语言·chatgpt
知识分享小能手12 分钟前
Java学习教程,从入门到精通,Java LinkedList(链表)语法知识点及案例代码(62)
java·大数据·开发语言·学习·链表·intellij-idea·大数据开发
ybq1951334543112 分钟前
javaEE--计算机是如何工作的-1
java·前端·java-ee
暗夜绿17 分钟前
【GoF23种设计模式】02_单例模式(Singleton Pattern)
java·spring·单例模式·设计模式
Nijika...25 分钟前
Spring整合Redis基本操作步骤
java·redis·后端·spring
Q_192849990630 分钟前
基于Spring Boot的水果蔬菜商城系统
java·spring boot
Ava_J40 分钟前
【Java入门指南 Day12:Java集合框架】
java·开发语言·python
Zyyyyu_1 小时前
捣鼓小玩意-分批处理工具类
java
付宇轩1 小时前
leetcode 3195.包含所有1的最小矩形面积I
java·算法·leetcode