【Design Pattern】Concurrency - Monitor Object Pattern

Posted by 西维蜀黍 on 2021-06-27, Last Modified on 2022-12-10

MONITOR OBJECT

Generally, Monitor Object is a pattern that controls the concurrent access of a method in an object. If there are some concurrent threads calling a method at the same time, only one thread can execute this method.

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

Example

Java has built-in support of concurrency. Every object in Java has an intrinsic lock, with synchronized keyword we can easily protect a critical section. We can also implement condition variables using wait/notify methods.

The following code is an example of Monitor Object. An instance of MonitorQueue could be shared by several threads. Both append and poll methods are protected by “synchronized” keyword so only one thread can execute them. If there is no element in the queue, threads calling poll method are suspended.

import java.util.LinkedList;

public class MonitorQueue {
  private LinkedList < Integer > queue = new LinkedList < > ();

  public synchronized void append(int num) throws InterruptedException {
    queue.addLast(num);
    notifyAll();
  }

  public synchronized int poll() throws InterruptedException {
    while (queue.size() == 0) {
      wait();
    }
    return queue.removeFirst();
  }
}

From Java 1.5, we are able to use ReentrantLock class to implement Monitor Object pattern instead of the intrinsic lock. The newCondition method of ReentrantLock creates condition variables we need.

Using ReentrantLock, we can rewrite the MonitorQueue class as below.

import java.util.LinkedList;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;

public class MonitorQueue {
  private LinkedList < Integer > queue = new LinkedList < > ();
  final Lock lock = new ReentrantLock();
  final Condition notEmpty = lock.newCondition();

  public void append(int num) throws InterruptedException {
    lock.lock();
    try {
      queue.addLast(num);
      notEmpty.signalAll();
    } finally {
      lock.unlock();
    }
  }

  public int poll() throws InterruptedException {
    lock.lock();
    try {
      while (queue.size() == 0) {
        notEmpty.await();
      }
    } finally {
      lock.unlock();
    }
    return queue.removeFirst();
  }
}

Reference