基于值而非对象的Java同步锁实现,求性能与安全缺陷评估
基于值的锁实现缺陷分析
安全类缺陷
- 锁有效性完全依赖T类型的equals/hashCode实现:你使用
HashSet存储锁的键值,若作为入参的T类型(比如自定义的账户类)没有正确重写equals和hashCode方法,会出现两个逻辑上相同的账号被判定为不同键的情况,锁直接失效,余额更新场景下会直接出现资损。另外HashSet初始化时使用了原始类型写法new HashSet(),没有指定泛型<T>,存在类型安全隐患。 - 无锁所有权校验:
Lock类没有记录持有锁的线程信息,任何拿到Lock实例的线程都可以调用release方法主动释放其他线程持有的锁,会导致多线程同时持有同值锁的线程安全问题。 - 不支持可重入:同一个线程重复申请同一个值的锁会直接死锁:首次申请已将值存入
HashSet,第二次申请时add失败进入wait状态,自身持有的锁永远无法释放。 - 锁泄漏风险极高:若使用者未在
finally块中调用release方法,对应的值会永久残留在HashSet中,后续所有请求该值锁的线程都会永久阻塞,等于对应账号完全无法操作。
性能类缺陷
- 全局锁导致完全串行化:
acquireLock方法直接加了类级别的synchronized锁,所有线程不管请求的值是否相同,都要抢同一把全局锁,完全无法利用并发能力。哪怕有上万个互不相关的账号,所有请求也必须串行排队,高并发下吞吐量极低。 - notifyAll引发严重惊群效应:释放锁时调用的
notifyAll会唤醒所有等待在全局锁上的线程,这些线程被唤醒后抢锁,绝大多数发现自己要的值仍被占用,又回到等待状态,会产生大量无效CPU消耗,并发越高性能损耗越严重。
可选优化方向
你可以参考分段锁的思路优化:
- 按值的哈希值分配分段锁,降低锁粒度,不同值的锁申请不需要抢全局锁
- 给
Lock类增加持有线程、重入计数字段,实现可重入和所有权校验 - 给每个值对应单独的
Condition,释放锁时只唤醒等待对应值的线程,避免惊群 - 增加锁超时自动释放机制,降低锁泄漏的影响
内容的提问来源于stack exchange,提问作者chaplean
相关产品推荐
相关产品推荐

