redis简单的分布式锁实现(乞丐版)

由于公司的redis版本较低,setnx无法同时设置有效时间,容易导致死锁,现写一种简易版分布式锁


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
/**
 * @author jiangwenzhang
 */
@Component
public class RedisLock {
    private static final Logger log =
            LoggerFactory.getLogger(RedisLock.class);
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 加锁
     *
     * @param lockKey   加锁的Key
     * @param lockValue 时间戳:当前时间+超时时间
     * @return
     */
    public boolean lock(String lockKey, String lockValue) {
        if (stringRedisTemplate.opsForValue().setIfAbsent(lockKey, lockValue)) {
            // 对应setnx命令,可以成功设置,也就是key不存在,获得锁成功
            return true;
        }

        //设置失败,获得锁失败
        // 判断锁超时 - 防止原来的操作异常,没有运行解锁操作 ,防止死锁
        String currentLock = stringRedisTemplate.opsForValue().get(lockKey);
        if (!StringUtils.isEmpty(currentLock)) {
            String[] split = currentLock.split(":");
            // 如果锁过期 currentLock不为空且小于当前时间
            if (!StringUtils.isEmpty(split[1]) && Long.parseLong(split[1]) < System.currentTimeMillis()) {
                //如果lockKey对应的锁已经存在,获取上一次设置的时间戳之后并重置lockKey对应的锁的时间戳
                String preLock = stringRedisTemplate.opsForValue().getAndSet(lockKey, lockValue);

                //假设多个线程同时进来这里,因为key被占用了,而且锁过期了。
                //获取的值currentLock=A(get取的旧的值肯定是一样的),多个线程的lockValue都不相同,key都是K.锁时间已经过期了。
                //而这里面的getAndSet只会有一个线程获取到A,其他线程获取的都不是A。
                if (!StringUtils.isEmpty(preLock) && preLock.equals(currentLock)) {
                    stringRedisTemplate.opsForValue().set(lockKey, lockValue);
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * 释放锁
     *
     * @param lockKey
     * @param lockValue
     */
    public void release(String lockKey, String lockValue) {
        try {
            String currentValue = stringRedisTemplate.opsForValue().get(lockKey);
            if (!StringUtils.isEmpty(currentValue) && currentValue.equals(lockValue)) {
                // 删除锁状态
                stringRedisTemplate.opsForValue().getOperations().delete(lockKey);
            }
        } catch (Exception e) {
            log.error("解锁异常:{}", e);
        }
    }
}

测试代码

import com.pateo.qingcloud.cp.config.RedisLock;
import com.pateo.qingcloud.cp.testvo.TestVo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.UUID;

@RunWith(SpringRunner.class)
@SpringBootTest
public class MscpchargeApplicationTests {

    @Autowired
    private RedisLock redisLock;

    @Test
    public void contextLoads() {
        StringBuilder sb = new StringBuilder();
        long time = System.currentTimeMillis() + 1000000;
        sb.append(UUID.randomUUID().toString());
        sb.append(":");
        sb.append(time);
        String lockValue = sb.toString();
        System.out.println(redisLock.lock("ssdsad", lockValue));
        System.out.println(redisLock.lock("ssdsad", lockValue));

        System.out.println(redisLock.lock("ssdsad", lockValue));

        System.out.println(redisLock.lock("ssdsad", lockValue));

    }

}
posted @   降温了  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示