Concurrency Patterns – Monitor Object

The pattern revolves around synchronization. In short, concurrent threads (clients) can only use the object via a set of synchronized methods. Only one method can run at a time. Typically a synchronized method watches for a certain condition. However, there is no polling involved. Instead, the methods are being notified. That’s an important difference in comparison to the Active Object.

Monitor Object is similar to the Active Object in a sense that it exposes a defined interface via object’s synchronized methods. On the other hand, the methods execute in the client’s thread as there is no notion of a centralised thread control. There is no significant performance overhead either, Since inefficient busy-waiting loops (polling) are replaced with notifications.

 

Key Components

  • Monitor Object: exposes synchronized methods as the only means of client access
  • Synchronized Methods: guarantee thread-safe access to the internal state
  • Monitor Lock: used by the synchronized methods to serialise method invocation
  • Monitor Condition: caters for cooperative execution scheduling

 

Advantages

    • Simplified synchronization: All of the hard work is offloaded to the object itself, clients are not concerned with synchronization issues.
  • Cooperative execution scheduling: Monitor conditions are used to suspend / resume method execution.
  • Reduced performance overhead: Notifications over inefficient polling.

 

Drawbacks

    • Synchronization tightly coupled with core logic: Synchronization code blends into the business logic which breaks the principle of separation of concerns.
  • Nested monitor lockout problem: An endless wait for a condition to become true can occur when a monitor object is nested into another kind of its own. In Java for example, monitor locks are not shared between two separate classes. Thus, it can happen that the outer monitor is never released and any threads watching that monitor would be kept waiting.

 

Example Implementation

Inspired by a blog post Java Monitor Pattern I came up with a hypothetical usage of a public toilet. Not only does it reminiscence of what makes us all equal, but it also comprises pattern’s dominant attributes. A toilet is either occupied or vacant, hence the locked / unlocked parallel. It also should only be used by a single person at a time (race conditions).
Only a vacant toilet can be entered. Once in, the visitor is granted to leave:
public interface Toilet {
   boolean enter();
   void quit();
   boolean isOccupied();
}

Now, the challenge is to ensure, under any circumstances, that the toilet only be used by a single person. Should that condition fail, the toilet becomes flooded:

public class ToiletFloodedException extends RuntimeException {..}

An obviously ignorant implementation of the Toilet interface ..:

public class FilthyToilet implements Toilet {
   // a concurrent visitor counter.
   private int counter = 0;
   ..
}
.. has unavoidable consequences: console output after having run the FilthyToiletMultiThreadedTest.java:
The toilet was flooded 25 times under a moderate load.
The toilet was flooded 38 times under a heavy load.
The toilet was flooded 96 times under an extreme load.
The correct implementation makes use of the Monitor Object pattern:
public class CleanToilet implements Toilet {
   // Monitor Lock used by the synchronized methods
   private final ReentrantLock lock;
   // Monitor Condition - the toilet can only
   // be used by a single person at a time
   private Condition oneAtATimeCondition;
   // The guarded object's state - the 'volatile' flag
   // is crucial for work signalling
   private volatile int counter;
   // all of the public methods are synchronized
   ..
}
The synchronization is ensured by using a lock along with a condition. The lock holds as long as the condition holds true:
 public boolean enter() {
  lock.lock();
  try {
    while (counter > 0) {
      // wait while the toilet is being used
      oneAtATimeCondition.awaitUninterruptibly();
    }
    if (++counter == 1) {
      // the toilet has been successfully acquired
      oneAtATimeCondition.signal();
    }
    return isOccupied();
  } finally {
    lock.unlock();
  }
}
Source Code

Resources

Similar Posts