1

I understand (somewhat) the features of the jdk 5 ReentrantLock here

But why we would want a 're-entrant' lock? i.e if a Thread already has the lock on an Object, why would it need to acquire it again?

neminem
  • 2,658
  • 5
  • 27
  • 36
Victor
  • 16,609
  • 71
  • 229
  • 409

2 Answers2

3

Consider this theoretical example: You are using a lock to protect some back-end data while updating some items in a list box in your GUI. You loop through and modify the items. While doing so, the list box fires an event (perhaps a Selection Changed event or something) for which you have a handler registered. This handler also locks the same lock in order to process the new item. If the lock is not recursive, this thread would deadlock on the second attempt to acquire the lock.

Dark Falcon
  • 43,592
  • 5
  • 83
  • 98
  • I know it's been almost 10 years, but allowing a event from listbox to change the data while i am looping through the data sounds like a pretty bad design. While I am holding the lock, I am not expecting the data to change unless I explicitly change it. That sounds like unexpected behaviour and should raise an exception instead. – Miigon Mar 17 '22 at 00:39
  • 1
    @Miigon: The lock is not necessarily just so the data can be modified. Sometimes it is so the data can be read. For example, It would not seem like an impossible or bad pattern for updating some other field on the form when the selection in the listbox changes. e.g. showing more detail about the selected item. If the data is updated by a background thread and shared with the UI, one might need a lock to prevent the UI from reading a mixture of old and new data being written by the background thread. It is NOT the UI event triggering the changes to the data. – Dark Falcon Mar 17 '22 at 16:31
2

Reentrant locks are useful in cases where a resource cannot tolerate all forms of arbitrarily-timed accesses, but can tolerate certain patterns of access which can occur in nested execution contexts. In many cases their usage is unaesthetic and sloppy, but it may be easier to arrange things so that a reentrant lock can be guaranteed to work than it would be to arrange things so as to make one unnecessary.

Note that while many languages default to making locks reentrant, that is not necessarily a good thing. If code acquires a lock and then other code in that thread tries to acquire a token for that same lock, it's clear that that having the second request wait until lock has been released isn't going to be very productive. That does not imply, however, that the second request should allow access to the lock. In many cases a proper course of action would be for the second request to throw an immediate exception (access shouldn't be granted until the lock is released, and that can't happen until either the request is granted (which shouldn't happen) or the code exits some other way (an exception being the most natural choice). Such a situation would apply if the a method which was modifying a lock-guarded data structure called some outside code which wasn't expected to use the data structure while the data structure was in an inconsistent state. If the code unexpectedly does try to use the data structure, having it fail immediately with an exception may be better than having it wait forever for a lock it's never going to get, or blithely proceed into a lock and access invalid data.

There are many cases where code will call nested routines at times when a guarded resource satisfies some but not all of its invariants, and where the outside code may expect the nested routines to make some kinds of changes to it but not others. In such cases, reentrant locks may be appropriate, but care is required to ensure that code doesn't do things it shouldn't. One advantage of reentrant locks is that if code which makes nested calls with the lock held sets flags to indicate its promises/requirements, and code which acquires the lock tests those flags on entry, one can guarantee that the flags will only be manipulated in predictable sequences. Such a thing would not be possible if two different threads were trying to use the resource simultaneously.

supercat
  • 77,689
  • 9
  • 166
  • 211