V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
JinTianYi456
V2EX  ›  Java

RedissonClient, 这种 lock 风格有隐患吗?

  •  
  •   JinTianYi456 · 107 天前 · 1319 次点击
    这是一个创建于 107 天前的主题,其中的信息可能已经有所发展或是发生改变。
    // 风格 1 (有隐患?)
    
    RLock lock = redissonClient.getLock("lock:1666270081");
    if (lock.tryLock()) {
        try {
            System.out.println("do in lock");
        } finally {
            lock.unlock();
        }
    }
    
    // 风格 2
    
    RLock lock = redissonClient.getLock("lock:1666270081");
    try {
        if (lock.tryLock()) {
            System.out.println("do in lock");
        }
    } finally {
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
    
    13 条回复    2022-10-25 14:51:43 +08:00
    potatowish
        1
    potatowish  
       107 天前 via iPhone
    这个 lock 如果被其他线程强制解锁再加锁,你这里 unlock 会抛异常
    JinTianYi456
        2
    JinTianYi456  
    OP
       107 天前
    @potatowish #1 没懂,细说
    redorblacck886
        3
    redorblacck886  
       107 天前
    建议这样
    if (lock.isLocked() && lock.isHeldByCurrentThread()) {
    lock.unlock();
    }
    JinTianYi456
        4
    JinTianYi456  
    OP
       107 天前
    @redorblacck886 #3 没必要吧
    isLocked: `Checks if the lock locked by any thread`
    isHeldByCurrentThread: `Checks if this lock is held by the current thread`
    所以只能 3 种情况吧,
    isLocked=true, isHeldByCurrentThread=true
    isLocked=true, isHeldByCurrentThread=false
    isLocked=false, isHeldByCurrentThread=false
    JinTianYi456
        5
    JinTianYi456  
    OP
       107 天前
    @potatowish #1 哦,你是说`风格 1`的,加锁了,然后我去敲命令删了那个 key ,然后 unlock 就报错了。忽略这种乱删 key 情况。主要是想比较 `风格 1`,先 lock ,然后进 try (这样有隐患吗)。`风格 2` 先 try ,再 lock 。
    beichenhpy
        6
    beichenhpy  
       107 天前
    我一般是先 try ,稳一点
    bthulu
        7
    bthulu  
       107 天前
    风格 1 就行了
    Chinsung
        8
    Chinsung  
       107 天前
    前排提示,如果不判断 lock.isHeldByCurrentThread()的话,unlock 会抛异常
    起码我用的 redisson 版本是这样
    JinTianYi456
        9
    JinTianYi456  
    OP
       106 天前
    @Chinsung #8 是指 5 楼里说到的情况?
    vvtf
        10
    vvtf  
       106 天前
    public final class RLockCloseable implements AutoCloseable {

    private RLock lock;
    private boolean locked;

    public RLockCloseable(RLock lock) {
    Objects.nonNull(lock);
    this.lock = lock;
    }

    public static RLockCloseable of(RLock lock) {
    return new RLockCloseable(lock);
    }

    public static void ifLocked(RLock lock, Consumer<Void> fn) {
    try (RLockCloseable _lock = new RLockCloseable(lock)) {
    if (_lock.tryLock()) {
    fn.accept(null);
    }
    }
    }

    public boolean tryLock() {
    // or use isHeldByCurrentThread
    return locked = lock.tryLock();
    }

    public void close() {
    if (locked) lock.unlock();
    }

    }


    // usage
    //1.
    try (RLockCloseable lock = redisson.getLock("key")) {
    if (lock.tryLock()) {
    // TODO
    }
    }

    //2.
    RLockCloseable.ifLocked(redisson.getLock("key"), _t -> {
    // TODO
    });
    Chinsung
        11
    Chinsung  
       104 天前   ❤️ 1
    @JinTianYi456 #9 并不是,而是第一个线程持有的锁如果因为执行时间过长超时,redis 锁的 key 失效了,第二个线程拿到了这个锁开始执行,此时第一个线程执行完了去释放锁的时候因为这个锁并不是他持有的,redisson 会抛异常,如果你写了事务,反而可能事务会因为这个异常回滚。
    哪怕是不手动设置过期时间用看门狗也不一定能完全避免这种情况,因为看门狗底层还是给 key 设了过期时间的,只是看门狗能保证这种情况的概率会很低很低
    JinTianYi456
        12
    JinTianYi456  
    OP
       103 天前
    @Chinsung #11 那我把`风格 1`的 unlock 也包上 isHeldByCurrentThread ,然后再比较`风格 1`,`风格 2`呢?
    Chinsung
        13
    Chinsung  
       102 天前
    @JinTianYi456 #12 第二种,tryLock 可能有异常
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   实用小工具   ·   1351 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 46ms · UTC 05:18 · PVG 13:18 · LAX 21:18 · JFK 00:18
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.