请问这段 Java 代码能保证线程安全吗

2022-09-30 15:00:50 +08:00
 0xcaffebabe

代码的意图是针对每个 lockKey 同一时刻只能有一个线程处理

private final Map<String, Object> lockMap = new ConcurrentHashMap<>(32);
...
synchronized (lockMap.computeIfAbsent(lockKey, key -> new Object())) {
	try {
    	...
    } catch(Exception e) {
    	...
    } finaly {
    	lockMap.remove(lockKey);
    }
}
4307 次点击
所在节点    Java
32 条回复
moshiyeap100
2022-09-30 15:12:55 +08:00
1. 如果是微服务,多服务的情况下不可以。
wolfie
2022-09-30 15:14:37 +08:00
不能

A 执行完 remove key => B 执行中 => C computeIfAbsent 拿 new Object();
moshiyeap100
2022-09-30 15:14:39 +08:00
如果不是微服务,直接 synchronized(key.intern()){},虽然常量池可能会越来越大。
ipwx
2022-09-30 15:16:57 +08:00
这问题很大。因为你 lockMap 本身没有锁,所以你在拿到 lock 对象前的操作都有问题。

你这需求很早有人就做过了。比如 https://yanbin.blog/google-guava-striped-key-based-fine-grain-locks/
slomo
2022-09-30 15:17:34 +08:00
em.... 直接在 computeIfAbsent 的第二个参数, 也就是 Function 里做处理, 最后返回 null, 是不是也可行
ipwx
2022-09-30 15:17:52 +08:00
简单来说就是不需要每个 key 一个锁。给一个固定大小的锁池,把 key 哈希映射到锁池里面。这样既能一定程度上分散锁,又不用动态创建新的锁,锁的总数也是确定的。
momocraft
2022-09-30 15:19:35 +08:00
如果不 remove 就能用吧, computeIfAbsent 保证一个 lockKey 最多只会创建一次 lock object
ipwx
2022-09-30 15:23:04 +08:00
@momocraft 虽然你说得对,但是 key 一多就内存爆炸了。

还是固定大小的锁池比较合理。
0xcaffebabe
2022-09-30 15:33:53 +08:00
学习到了,Guava 的 Striped ,感谢各位
dqzcwxb
2022-09-30 15:34:45 +08:00
xiang0818
2022-09-30 16:16:39 +08:00
@moshiyeap100 这个和微服务有啥关系,这是单机 map
imaple
2022-09-30 16:25:25 +08:00
没问题啊
rqxiao
2022-09-30 16:32:46 +08:00
remove 为什么会线程不安全啊
hsymlg
2022-09-30 16:48:37 +08:00
这代码为什么会有线程安全问题啊🤔
zhulixin
2022-09-30 17:26:24 +08:00
remove 代表锁被移除了,此时 sync 中的 obj 还没有解锁。这时候其他线程 get 同样的 key ,就拿到了新锁。即同一个 Key 产生了多把锁。
7911364440
2022-09-30 17:39:05 +08:00
@zhulixin 如果把 remove 放在 synchronized 外面应该就没问题了吧


```java
private final Map<String, Object> lockMap = new ConcurrentHashMap<>(32);
...
try {
synchronized (lockMap.computeIfAbsent(lockKey, key -> new Object())) {
...
}
} catch(Exception e) {
...
} finaly {
lockMap.remove(lockKey);
}

```
cubecube
2022-09-30 17:42:44 +08:00
@dqzcwxb 一般情况下不要用 intern ,有严重的性能问题,也没啥收益。现在 GC 都有 String Deduplication 了
ainyyy
2022-09-30 18:24:07 +08:00
@7911364440 还是一样的问题吧,第一次锁释放 remove 前,第二个 key 获取到锁进入执行,执行过程中被释放,还是会生成新的锁对象
clownpiece
2022-09-30 20:01:18 +08:00
if (lockMap.computeIfAbsent(lockKey, k -> new AtomicBoolean()).compareAndSet(false, true)) {
try {
// ...
} finally {
lockMap.remove(lockKey);
}
}
leonshaw
2022-09-30 20:07:48 +08:00
从 map 里拿到对象到加锁中间有窗口,无法保证加锁时对象还在 map 里。

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

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

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

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

© 2021 V2EX