2

我正在使用 Mysql GET_LOCK在分布式系统中实现锁定服务。在调用我的 getLock() 方法时,如果客户端获得了锁,我会在数据库中创建一个条目,并在释放锁时删除该条目。

假设调用客户端将在达到其目的后释放锁。但是,我想确保在客户端不释放它或不进行适当清理的情况下释放锁。

一种方法是在我的锁对象上使用 finalize 方法在调用 finalize 时释放它。然而它并不理想,增加了复杂性并且在 Java 9 中被弃用。我读到了 Phantom 引用,它比终结器更好,但它的复杂性也很高。我把它作为我最后的手段。

有没有更简单、更少依赖 JVM 的方法来处理这个用例?

4

1 回答 1

7

不要这样做。问题大于好处。

不管你是使用finalization、Reference API,还是a Cleaner(基于Reference API),试图使用对象的生命周期来控制锁都会产生更多的问题。

无法保证对象会被垃圾回收。只要有足够的空闲堆内存,JVM 就没有理由让垃圾收集器运行。此外,即使垃圾收集器运行,也不能保证它会收集所有无法访问的对象。像 G1GC 这样的垃圾收集器专注于在给定的时间限制内尽可能多地回收内存,优先考虑对象年龄的影响。事实上,他们甚至不知道一个死物在周围躺了多久。

因此,当 JVM 对 GC 收集一堆较新的对象感到满意时,一个特定的无法访问的对象可能会无限期地保持未被收集。

更糟糕的是,对象有可能比预期更早地收集垃圾。对于显式锁定和解锁操作,这没有影响,因为程序的行为保持不变。但是,当您将对象的生命周期与解锁操作联系起来时,您就有麻烦了。这尤其适用于当对象仅服务于锁定和解锁动作并且解锁动作已被应用程序程序员忘记时,换句话说,锁定对象在执行锁定动作后完全未被使用。

为了防止提前收集,您需要类似的东西Reference.reachabilityFence(lockObject),但是当程序员忘记执行所需的解锁操作时,他们记住插入必要的可达性围栏的可能性有多大?他们更容易插入所需的解锁操作。

解决这个问题的任何尝试都远非简单,同时仍无法提供值得努力的保证。你最好强烈提醒你的锁类的用户需要解锁它。考虑在try-with-resource块中实施AutoCloseable和宣传使用它。

于 2020-06-30T08:10:50.300 回答