Movatterモバイル変換


[0]ホーム

URL:


Tech & Media Labs
Home
RSS
Java Concurrency
  1. Java Concurrency and Multithreading Tutorial
  2. Multithreading Benefits
  3. Multithreading Costs
  4. Concurrency Models
  5. Same-threading
  6. Single-threaded Concurrency
  7. Concurrency vs. Parallelism
  8. Creating and Starting Java Threads
  9. Java Virtual Threads
  10. Race Conditions and Critical Sections
  11. Thread Safety and Shared Resources
  12. Thread Safety and Immutability
  13. Java Memory Model
  14. Java Happens Before Guarantee
  15. Java Synchronized Blocks
  16. Java Volatile Keyword
  17. CPU Cache Coherence in Java Concurrency
  18. False Sharing in Java
  19. Java ThreadLocal
  20. Thread Signaling in Java
  21. Deadlock
  22. Deadlock Prevention
  23. Starvation and Fairness
  24. Nested Monitor Lockout
  25. Slipped Conditions
  26. Locks in Java
  27. Read / Write Locks in Java
  28. Reentrance Lockout
  29. Semaphores
  30. Blocking Queues
  31. The Producer Consumer Pattern
  32. Thread Pools
  33. Thread Congestion in Java
  34. Compare and Swap
  35. Anatomy of a Synchronizer
  36. Non-blocking Algorithms
  37. Amdahl's Law
  38. Java Concurrency References

Nested Monitor Lockout

Jakob Jenkov
Last update: 2018-04-04

How Nested Monitor Lockout Occurs

Nested monitor lockout is a problem similar to deadlock. A nested monitor lockout occurs like this:

Thread 1 synchronizes on AThread 1 synchronizes on B (while synchronized on A)Thread 1 decides to wait for a signal from another thread before continuingThread 1 calls B.wait() thereby releasing the lock on B, but not A.Thread 2 needs to lock both A and B (in that sequence)        to send Thread 1 the signal.Thread 2 cannot lock A, since Thread 1 still holds the lock on A.Thread 2 remain blocked indefinately waiting for Thread1        to release the lock on AThread 1 remain blocked indefinately waiting for the signal from        Thread 2, thereby        never releasing the lock on A, that must be released to make        it possible for Thread 2 to send the signal to Thread 1, etc.

This may sound like a pretty theoretical situation, but look at the naiveLock implemenation below:

//lock implementation with nested monitor lockout problempublic class Lock{  protected MonitorObject monitorObject = new MonitorObject();  protected boolean isLocked = false;  public void lock() throws InterruptedException{synchronized(this){      while(isLocked){synchronized(this.monitorObject){            this.monitorObject.wait();}      }      isLocked = true;}  }  public void unlock(){synchronized(this){      this.isLocked = false;synchronized(this.monitorObject){        this.monitorObject.notify();}}  }}

Notice how thelock() method first synchronizes on "this", then synchronizes on themonitorObject member. IfisLocked is false there is no problem. The thread does not callmonitorObject.wait(). IfisLocked is true however, the thread callinglock() is parked waiting in themonitorObject.wait() call.

The problem with this is, that the call tomonitorObject.wait() only releases the synchronization monitor on themonitorObject member, and not the synchronization monitor associated with "this". In other words, the thread that was just parked waiting is still holding the synchronization lock on "this".

When the thread that locked theLock in the first place tries to unlock it by callingunlock() it will be blocked trying to enter thesynchronized(this) block in theunlock() method. It will remain blocked until the thread waiting inlock() leaves thesynchronized(this) block. But the thread waiting in thelock() method will not leave that block until theisLocked is set to false, and amonitorObject.notify() is executed, as it happens inunlock().

Put shortly, the thread waiting inlock() needs anunlock() call to execute successfully for it to exitlock() and the synchronized blocks inside it. But, no thread can actually executeunlock() until the thread waiting inlock() leaves the outer synchronized block.

This result is that any thread calling eitherlock() orunlock() will become blocked indefinately. This is called a nested monitor lockout.

A More Realistic Example

You may claim that you would never implement a lock like the one shown earlier. That you would not callwait() andnotify() on an internal monitor object, but rather on the This is probably true. But there are situations in which designs like the one above may arise. For instance, if you were to implementfairness in a Lock. When doing so you want each thread to callwait() on each their own queue object, so that you can notify the threads one at a time.

Look at this naive implementation of a fair lock:

//Fair Lock implementation with nested monitor lockout problempublic class FairLock {  private boolean           isLocked       = false;  private Thread            lockingThread  = null;  private List<QueueObject> waitingThreads =            new ArrayList<QueueObject>();  public void lock() throws InterruptedException{    QueueObject queueObject = new QueueObject();synchronized(this){      waitingThreads.add(queueObject);      while(isLocked || waitingThreads.get(0) != queueObject){synchronized(queueObject){          try{            queueObject.wait();          }catch(InterruptedException e){            waitingThreads.remove(queueObject);            throw e;          }}      }      waitingThreads.remove(queueObject);      isLocked = true;      lockingThread = Thread.currentThread();}  }  publicsynchronized void unlock(){    if(this.lockingThread != Thread.currentThread()){      throw new IllegalMonitorStateException(        "Calling thread has not locked this lock");    }    isLocked      = false;    lockingThread = null;    if(waitingThreads.size() > 0){      QueueObject queueObject = waitingThreads.get(0);synchronized(queueObject){        queueObject.notify();}    }  }}
public class QueueObject {}

At first glance this implementation may look fine, but notice how thelock() method callsqueueObject.wait(); from inside two synchronized blocks. One synchronized on "this", and nested inside that, a block synchronized on thequeueObject local variable. When a thread callsqueueObject.wait()it releases the lock on theQueueObject instance, but not the lock associated with "this".

Notice too, that theunlock() method is declared synchronized which equals asynchronized(this) block. This means, that if a thread is waiting insidelock() the monitor object associated with "this" will be locked by the waiting thread. All threads callingunlock() will remain blocked indefinately, waiting for the waiting thread to release the lock on "this". But this will never happen, since this only happens if a thread succeeds in sending a signal to the waiting thread, and this can only be sent by executing theunlock() method.

And so, the FairLock implementation from above could lead to nested monitor lockout. A better implementation of a fair lock is described in the textStarvation and Fairness.

Nested Monitor Lockout vs. Deadlock

The result of nested monitor lockout and deadlock are pretty much the same: The threads involved end up blocked forever waiting for each other.

The two situations are not equal though. As explained in the text onDeadlock a deadlock occurs when two threads obtain locks in different order. Thread 1 locks A, waits for B. Thread 2 has locked B, and now waits for A. As explained in the text onDeadlock Prevention deadlocks can be avoided by always locking the locks in the same order (Lock Ordering). However, a nested monitor lockout occurs exactly by two threads taking the locksin the same order. Thread 1 locks A and B, then releases B and waits for a signal from Thread 2. Thread 2 needs both A and B to send Thread 1 the signal. So, one thread is waiting for a signal, and another for a lock to be released.

The difference is summed up here:

In deadlock, two threads are waiting for each other to release locks.In nested monitor lockout, Thread 1 is holding a lock A, and waitsfor a signal from Thread 2. Thread 2 needs the lock A to send thesignal to Thread 1.
Next:Slipped Conditions

Jakob Jenkov

Featured Videos

Java ConcurrentMap + ConcurrentHashMap

Java Generics

Java ForkJoinPool

P2P Networks Introduction

















Copyright Jenkov Aps
Close TOC
All Tutorial Trails
All Trails
Table of contents (TOC) for this tutorial trail
Trail TOC
Table of contents (TOC) for this tutorial
Page TOC
Previous tutorial in this tutorial trail
Previous
Next tutorial in this tutorial trail
Next

[8]ページ先頭

©2009-2025 Movatter.jp