2

I am implementing a locking service in a distributed system using Mysql GET_LOCK. Upon calling my getLock() method, if a lock is obtained by a client, I make an entry to DB and delete the entry when lock is released.

Assumption is calling client would release the lock once its purpose is served. However, I would like to ensure that lock gets released in case client does not release it or doesn't do proper cleanup.

One way would be to use finalize method on my lock object to release it when finalize is called. However it's not ideal, adds complications and is deprecated in Java 9. I read about Phantom references which are better than finalizers, but its complexity level is also high. I am keeping it as my last resort.

Is there any simpler, less JVM dependent way to handle this usecase?

Naman
  • 27,789
  • 26
  • 218
  • 353
dhamu
  • 605
  • 1
  • 7
  • 17
  • Is Java your main system? If so, it has a rich locking sub-system. Why do you need to use DB for these purposes? – PM 77-1 Jun 29 '20 at 23:03
  • That's a good point. However unfortunately we use java 7 and currently for our use case mysql get_lock suffices and it's not possible to change it at this point. – dhamu Jun 29 '20 at 23:35
  • there is the `Cleaner API`, but only since java-9. – Eugene Jun 30 '20 at 02:47
  • Related reads - [Why is the finalize() method deprecated in Java 9?](https://stackoverflow.com/questions/56139760/why-is-the-finalize-method-deprecated-in-java-9), [Should Java-9 Cleaner be preferred over finalization](https://stackoverflow.com/questions/52879761/should-java-9-cleaner-be-preferred-to-finalization) and [Why would you ever implement finalize()?](https://stackoverflow.com/questions/158174/why-would-you-ever-implement-finalize) – Naman Jul 07 '20 at 17:27

1 Answers1

8

Don’t do this. The problems outweigh the benefits.

Regardless of whether you are using finalization, the Reference API, or a Cleaner (which settles on the Reference API), trying to use an object’s lifecycle to control a lock will create even more problems.

There is no guaranty that an object will ever get garbage collected. As long as there is enough free heap memory left, a JVM has no reasons to let the garbage collector run. Further, even when a garbage collector runs, there is no guaranty that it will collect all unreachable objects. Garbage collectors like G1GC focus on reclaiming as much memory as they can within given time constraints, prioritizing effect over object ages. In fact, it’s not even known to them, how long a dead object is lying around.

So a particular unreachable object may stay uncollected for an infinite time while the JVM is happy with the GC collecting a bunch of newer objects.

Even worse, there is the possibility for an object to 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. This applies especially, when the object only serves the lock & unlock actions and the unlock action has been forgotten by the application programmer, in other words, the lock object is entirely unused after performing the lock action.

To prevent early collection, you need something like Reference.reachabilityFence(lockObject), but when the programmer forgets to do the required unlock action, how likely are they to remember to insert the necessary reachability fence? It would be easier for them to insert the required unlock action instead.

Any attempt to solve this problems will be far away from being simple while still not providing guarantees that were worth the effort. You better remind the users of your lock class strongly on the need to unlock it. Consider implementing AutoCloseable and advertise using it in a try-with-resource block.

Holger
  • 285,553
  • 42
  • 434
  • 765