我正在使用 Mysql 在分布式系统中实现锁定服务 GET_LOCK .在调用我的 getLock() 方法时,如果客户端获得锁,我会在数据库中创建一个条目,并在释放锁时删除该条目。
假设调用客户端会在达到目的后释放锁。但是,我想确保在客户端未释放锁或未进行适当清理的情况下释放锁。
一种方法是在我的锁定对象上使用 finalize 方法,以便在调用 finalize 时释放它。然而,它并不理想,增加了复杂性,并在 Java 9 中被弃用。我读到过比终结器更好的 Phantom 引用,但它的复杂度也很高。我将保留它作为最后的手段。
有没有更简单、更少依赖 JVM 的方法来处理这个用例?
最佳答案
不要这样做。问题大于好处。
无论您使用的是终结化、Reference API 还是 Cleaner
(基于 Reference API),尝试使用对象的生命周期来控制锁都会产生更多问题。
不保证对象会被垃圾回收。只要有足够的可用堆内存,JVM 就没有理由让垃圾收集器运行。此外,即使垃圾收集器运行,也不能保证它会收集所有无法访问的对象。像 G1GC 这样的垃圾收集器专注于在给定的时间限制内回收尽可能多的内存,优先考虑对象年龄的影响。事实上,他们甚至不知道一个死物体在周围躺了多久。
因此,当 JVM 对 GC 收集一堆较新的对象感到满意时,一个特定的无法访问的对象可能会无限期地保持未收集状态。
更糟糕的是,对象有可能get garbage collected earlier than expected .对于显式锁定和解锁操作,这没有影响,因为程序的行为保持不变。但是,当您将对象的生命周期与解锁操作联系起来时,you’re in trouble .这尤其适用于当对象仅服务于锁定和解锁操作而应用程序员忘记了解锁操作时,换句话说,锁定对象在执行锁定操作后完全未被使用。
为了防止提前收集,您需要像 Reference.reachabilityFence(lockObject)
这样的东西,但是当程序员忘记执行所需的解锁操作时,他们记得插入必要的可达性栅栏的可能性有多大?他们插入所需的解锁操作会更容易。
任何解决此问题的尝试都远非简单,同时仍然无法提供值得付出努力的保证。你最好强烈提醒你的锁类的用户需要解锁它。考虑实现 AutoCloseable
并在 try-with-resource 中使用它做广告 block 。
关于java - Java终结器的替代品,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62647855/