Computers have been my hobby since I was 12. Now I'm a freelance Java developer. Like many other developers I am working on various private projects. Some are open source components (Butterfly Components - DI container, web ui, persistence api, mock test api etc.). Some are the tutorials at tutorials.jenkov.com. Yet others are web projects. I hold a bachelor degree in computer science and a master degree in IT focused on P2P networks. Jakob has posted 35 posts at DZone. You can read more from them at their website. View Full User Profile

Java Concurrency: Reentrance Lockout

06.14.2008
| 7243 views |
  • submit to reddit

Reentrance lockout is a situation similar to deadlock and nested monitor lockout.

Note: This text on reentrance lockout is part of my tutorial on Java Concurrency which by now contains 17 texts. Reentrance lockout is also covered in part in the texts on Locks and Read / Write Locks.

Reentrance lockout may occur if a thread reenters a Lock, ReadWriteLock or some other synchronizer that is not reentrant. Reentrant means that a thread that already holds a lock can retake it. Java's synchronized blocks are reentrant. Therefore the following code will work without problems:

public class Reentrant{

public synchronized outer(){
inner();
}

public synchronized inner(){
//do something
}
}

Notice how both outer() and inner() are declared synchronized, which in Java is equivalent to a synchronized(this) block. If a thread calls outer() there is no problem calling inner() from inside outer(), since both methods (or blocks) are synchronized on the same monitor object ("this"). If a thread already holds the lock on a monitor object, it has access to all blocks synchronized on the same monitor object. This is called reentrance. The thread can reenter any block of code for which it already holds the lock.

The following Lock implementation is not reentrant:

public class Lock{

private boolean isLocked = false;

public synchronized void lock()
throws InterruptedException{
while(isLocked){
wait();
}
isLocked = true;
}

public synchronized void unlock(){
isLocked = false;
notify();
}
}

If a thread calls lock() twice without calling unlock() in between, the second call to lock() will block. A reentrance lockout has occurred.

To avoid reentrance lockouts you have two options:

  1. Avoid writing code that reenters locks
  2. Use reentrant locks

Which of these options suit your project best depends on your concrete situation. Reentrant locks often don't perform as well as non-reentrant locks, and they are harder to implement, but this may not necessary be a problem in your case. Whether or not your code is easier to implement with or without lock reentrance must be determined case by case.

References
Published at DZone with permission of its author, Jakob Jenkov. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

David Voo replied on Sat, 2008/06/14 - 8:20pm

May I ask in what sort of screnario this will happen?

Jakob Jenkov replied on Sun, 2008/06/15 - 3:55am

This may happen if you implement a reentrant lock incorrectly. We had that discussion related to the post on read / write locks. The first implementation I had put together had a problem with reentrance, even if I tried to design it for reentrance. Here is that post and discussion:

http://java.dzone.com/news/java-concurrency-read-write-lo

 

It can also happen in your code if you have code like this:

 

 

 public class Reentrant{

private Lock lock = new Lock();

public outer(){
lock.lock();
//.. do something with exclusive access
inner();
//.. do something with exclusive access
lock.unlock();
}

public inner(){
lock.lock();
// ...
lock.unlock();
}
}

The outer() method is an example of a method that needs exclusive access but also needs to call another method with exclusive access. If the Lock used is not reentrant a call to outer() will result in a reentrance lockout.

Jakob Jenkov replied on Sun, 2008/06/15 - 4:12am

Of course, in this case you could also rewrite the Reentrant class to avoid calling one exclusive access method from inside another. Like this:

 public class Reentrant{  

private Lock lock = new Lock();

public outer(){
lock.lock();
//.. do something with exclusive access
doInner();
//.. do something with exclusive access
lock.unlock();
}

public inner(){
lock.lock();
doInner();
lock.unlock();
}

private doInner(){
//do inner stuff.
}
}

Now you could use a non-reentrant Lock, since outer() calls doInner() directly instead of going through the exclusive access inner() method. That is an example of avoiding reentrance.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.