Java 终结器的替代方案

Alternative to Java finalizer

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

假设调用客户端将在达到目的后释放锁。但是,我想确保锁被释放,以防客户端不释放它或没有进行适当的清理。

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

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

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

不管你是使用finalization,ReferenceAPI,还是一个Cleaner(定居在ReferenceAPI),试图用一个对象的生命周期来控制一个锁定会产生更多问题。

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

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

更糟糕的是,对象有可能get garbage collected earlier than expected. For explicit lock and unlock actions, this has no impact, as the behavior of the program stays the same. But when you connect the object’s lifecycle with an unlock action, you’re in trouble。这尤其适用于当对象仅服务于锁定和解锁操作并且解锁操作已被应用程序程序员忘记时,换句话说,锁定对象在执行锁定操作后完全未被使用。

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

任何解决此问题的尝试都远非简单,同时仍未提供值得付出努力的保证。你最好强烈提醒你的锁 class 的用户需要解锁。考虑实施 AutoCloseable and advertise using it in a try-with-resource 块。