请问这段 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);
    }
}
4331 次点击
所在节点    Java
32 条回复
yhvictor
2022-09-30 21:23:45 +08:00
这操作太骚气了,得看源代码才知道。
但是重入不是等待而是直接丢弃,这样不太好吧。
gosidealone
2022-09-30 21:49:49 +08:00
这为什么有问题呢? remove 之后逻辑已经走完了,即使有相同的 key 进来也不会影响吧。
moshiyeap100
2022-09-30 22:38:22 +08:00
@xiang0818 我的意思是微服务多实例部署的情况下,单机锁是无效的,如果同一个 key 请求到两个服务上,实际上两个服务都会处理。
xiangyuecn
2022-09-30 22:42:10 +08:00
没一点卵用的,同一个 key 会出现并发的情况简直就是混乱无比:

- 线程 1 创建了 object ,线程 2 等待线程 1 ,这是没有争议的

- 线程 1 执行完了,线程 2 开始执行,此时新来一个线程 3 将会如何执行?

- 线程 2 执行完后,它 remove 到底 remove 掉了谁的 object ?此时再新来一个线程 4 ,线程 3 和线程 4 又是怎么执行情况?
xiangyuecn
2022-09-30 22:47:58 +08:00
synchronized (lockMap.computeIfAbsent(lockKey, key -> new Object()))

这句本身就是线程不安全😂,线程 1 辛辛苦苦创建了一个 object ,写入进去了,但还没来得及返回上锁,被线程 2 抢走做核酸了,虽然罕见,但实属大冤种😂
14104chk
2022-10-01 09:08:49 +08:00
lockMap.remove(lockKey); 之后,锁就没有了,这时候当前线程 a 更改的变量可能还没同步到主内存,
同时又有另一个线程 b 获取锁,b 从主内存读取数据,因为这时候线程 a 的数据没有同步回主内存,所以 b 读到的还是旧数据
如果要使这段代码是线程安全的,就要给涉及的变量加上 volatile ,让变量的更新直接在主内存进行
7911364440
2022-10-01 09:53:53 +08:00
@leonshaw 锁对象就算不在 map 里感觉也没有影响吧
fallingg
2022-10-01 12:09:30 +08:00
map 是 concurrentmap ,所以对同一个 key 只会有一个线程去执行 try block 中的语句。但#26 可能说的是对的,在 remove 后,对同一个 key 不同线程可能上锁的不是同一个对象,这时候线程 a 的数据对 b 来说可能不可见。这段代码里如果去掉 remove key 的语句应该就是线程安全了。但是这样的话,可能会出现 key 特别多的场景,内存上会有问题。所以如果 key 的数量是有限的话,去掉 remove 语句后可以用。
fallingg
2022-10-01 12:11:02 +08:00
不过即使 remove key ,map 因此而扩容的数组应该也没无法释放 长期可能会有内存泄漏的现象。所以楼上池化的想法也不错
tairan2006
2022-10-01 15:00:35 +08:00
我记得有个编码规范是不要在 mapping function 里面再次更新当前 map 啊…而且你用了并发安全的容器,再用锁不就性能更差了?

一个简单的方案:在外侧创建一个 uuid ,去掉`synchronized`,直接用`computeIfAbsent`放入 uuid ,后面用 removeIf 判断 value 是这个 uuid 的话,移除掉 key 就行了。
Aresxue
2022-10-08 10:54:26 +08:00
@ipwx 这种是不是比较依赖 hash 算法,不然 hash 碰撞了不就 g 了吗?拿时间换空间?
ipwx
2022-10-10 09:32:52 +08:00
@Aresxue 锁太多了更慢。

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

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

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

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

© 2021 V2EX