Redis 实现分布式锁

V1: setnx命令

一般是使用 setnx(set if not exists) 指令,只允许被一个客户端占用。
先来先占,用完了,再调用del指令释放。
但是有个问题,如果逻辑执行到中间出现异常了,可能会导致del指令没有被调用,这样就会陷入死锁,锁永远得不到释放。

V2: 锁超时释放

所以在拿到锁之后,再给锁加上一个过期时间,比如5s,这样即使中间出现异常也可以保证5秒之后锁会自动释放。
但是如果在setnx和expire之间服务器进程突然挂掉了,就会导致expire得不到执行,也会造成死锁。
这种问题的根源就在于setnx和expire是两条指令而不是原子指令。如果这两条指令,可以一起执行就不会出现问题。

V3:set指令

这个问题在Redis2.8版本中得到了解决,这个版本加入了set指令的扩展参数,使得setnx和expire指令可以一起执行。
上面这个指令就是setnx和expire组合在一起的原子指令

// v1
public class RedisLock {  
    @Autowired  
    StringRedisTemplate stringRedisTemplate;  
    private String name;  
    private Double timeoutSec;  
    private static final String KEY_PREFIX = "lock:";  
    public boolean tryLock(long timeoutSec) {  
        //获取线程标示  
        Long threadId = Thread.currentThread().getId();  
        //获取锁  
        Boolean success = stringRedisTemplate.opsForValue()  
                .setIfAbsent(KEY_PREFIX + name, threadId + "", timeoutSec, TimeUnit.SECONDS);  
        return Boolean.TRUE.equals(success);  
    }  
  
    public void unlock() {  
        //释放锁  
        stringRedisTemplate.delete(KEY_PREFIX + name);  
    }  
}

v4:解决误删问题

线程1进行加锁 但是由于意外原因发送阻塞,没能在设置key过期时间内进行操作。锁自动释放
线程2之后进行加锁,然后正常处理业务。。。
线程1恢复正常 执行完成后直接删除了key,线程1误删了线程2 的锁

为此应该加入验证机制,释放锁之前检查是否是当前线程设置的锁

使用UUID+线程id

private static final string ID_PREFIX = UUID.randomUUID().tostring(true);

@Override
public boolean tryLock(long timeoutSec) {
    // 获取线程标示
    String threadId = ID_PREFIX + Thread.currentThread().getId();
    // 获取锁
    Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(
        KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS
    );
    return Boolean.TRUE.equals(success);
}

@Override
public void unlock() {
    // 获取线程标示
    String threadId = ID_PREFIX + Thread.currentThread().getId();
    // 获取锁中的标示
    String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);
    // 判断标示是否一致
    if (threadId.equals(id)) {
        // 释放锁
        stringRedisTemplate.delete(KEY_PREFIX + name);
    }
}

v5:lua脚本保证原子性

判断锁和释放锁之间出现了堵塞,也会导致释放其他线程的锁
可以使用lua脚本保证判断锁和释放锁的原子性

Pasted image 20240831224908.png

-- 比较线程标示与锁中的标示是否一致  
if(redis.call('get',KEYS[1]) == ARGV[1]) then  
    -- 释放锁del key  
    return redis.call('del',KEYS[1])  
end  
return 0

private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;  
static {  
    UNLOCK_SCRIPT = new DefaultRedisScript<>();  
    UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));  
    UNLOCK_SCRIPT.setResultType(Long.class);  
}  
  
public void unlock() {  
    //执行lua脚本  
    stringRedisTemplate.execute(  
            UNLOCK_SCRIPT,  
            Collections.singletonList(KEY_PREFIX + name),  
            ID_PREFIX + Thread.currentThread().getId()  
    );  
    //释放锁  
    stringRedisTemplate.delete(KEY_PREFIX + name);  
}

基本完成了一个生产可用的分布式锁

评论

  1. Macintosh Chrome
    5 天前
    2026-5-11 7:50:16

    Honestly, the deep dive into systems like this reminds me of analyzing betting patterns. It’s all about the structure. Checking out jilitt2026 games seems pretty comprehensive, especially with the payment options mentioned. Solid tech, I’d say.

  2. Macintosh Chrome
    2 周前
    2026-4-30 15:58:05

    Roulette strategy is fascinating – balancing risk & reward! Seeing platforms like jlslot app download offer quick withdrawals (5-15 mins!) definitely adds to the thrill. Responsible gaming is key, though – setting limits is smart!

  3. Windows Firefox
    3 月前
    2026-2-22 3:19:17

    Solid article! Thinking about bankroll management & variance is key in tournaments. Seeing platforms like jili pg app casino offer easy access via their app is a big plus for consistent play, too. Good read!

  4. Windows Chrome
    3 月前
    2026-2-20 15:13:58

    It’s easy to get carried away with online gaming! Remember responsible bankroll management-it’s key. Platforms like the 68win app casino offer diverse options, but set limits before you play. Staying in control is vital for enjoyment.

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇