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

2022-10-20 21:30:24 +08:00
 JinTianYi456
// 风格 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();
    }
}
2219 次点击
所在节点    Java
13 条回复
potatowish
2022-10-20 21:34:02 +08:00
这个 lock 如果被其他线程强制解锁再加锁,你这里 unlock 会抛异常
JinTianYi456
2022-10-20 21:37:12 +08:00
@potatowish #1 没懂,细说
Red998
2022-10-20 21:38:40 +08:00
建议这样
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
JinTianYi456
2022-10-20 21:49:09 +08:00
@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
2022-10-20 21:53:58 +08:00
@potatowish #1 哦,你是说`风格 1`的,加锁了,然后我去敲命令删了那个 key ,然后 unlock 就报错了。忽略这种乱删 key 情况。主要是想比较 `风格 1`,先 lock ,然后进 try (这样有隐患吗)。`风格 2` 先 try ,再 lock 。
uselessVisitor
2022-10-20 22:12:21 +08:00
我一般是先 try ,稳一点
bthulu
2022-10-21 11:05:44 +08:00
风格 1 就行了
Chinsung
2022-10-21 11:47:32 +08:00
前排提示,如果不判断 lock.isHeldByCurrentThread()的话,unlock 会抛异常
起码我用的 redisson 版本是这样
JinTianYi456
2022-10-21 14:09:18 +08:00
@Chinsung #8 是指 5 楼里说到的情况?
vvtf
2022-10-21 17:33:15 +08:00
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
2022-10-24 10:38:43 +08:00
@JinTianYi456 #9 并不是,而是第一个线程持有的锁如果因为执行时间过长超时,redis 锁的 key 失效了,第二个线程拿到了这个锁开始执行,此时第一个线程执行完了去释放锁的时候因为这个锁并不是他持有的,redisson 会抛异常,如果你写了事务,反而可能事务会因为这个异常回滚。
哪怕是不手动设置过期时间用看门狗也不一定能完全避免这种情况,因为看门狗底层还是给 key 设了过期时间的,只是看门狗能保证这种情况的概率会很低很低
JinTianYi456
2022-10-24 13:22:32 +08:00
@Chinsung #11 那我把`风格 1`的 unlock 也包上 isHeldByCurrentThread ,然后再比较`风格 1`,`风格 2`呢?
Chinsung
2022-10-25 14:51:43 +08:00
@JinTianYi456 #12 第二种,tryLock 可能有异常

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/888537

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX