AlgoMaster Logo

Monitor Object Pattern

16 min readUpdated December 16, 2025
Listen to this chapter
Unlock Audio

Monitor Object Pattern Explained

You're building a bank account system. Multiple threads need to deposit and withdraw money. The balance must never go negative and updates must be atomic.

You could expose the balance field and have each caller acquire locks before accessing it. But now every piece of code that touches an account must remember to lock correctly. Miss a lock somewhere, and you have a race condition. Forget to release it, and you have a deadlock.

The Monitor Object Pattern solves this by encapsulating synchronization within the object itself. The account object owns its lock and all operations automatically synchronize. Callers just call deposit() or withdraw(). They don't know or care about locks. The object guarantees thread safety internally.

In this article, we'll explore:

  • What is the Monitor Object Pattern?
  • The problem it solves
  • How it works
  • Implementation details
  • Condition variables and wait/notify
  • Common pitfalls
  • When to use it in LLD interviews

If you're enjoying this newsletter and want to get even more value, consider becoming a [paid subscriber](https://blog.algomaster.io/subscribe).

As a paid subscriber, you'll unlock all premium articles and gain full access to all [premium courses](https://algomaster.io/newsletter/paid/resources) on [algomaster.io](https://algomaster.io).

Unlock Full Access

1. What is the Monitor Object Pattern?

The Monitor Object Pattern is a synchronization pattern where an object encapsulates both its data and the synchronization needed to access that data safely. All public methods that access shared state are automatically synchronized. The object acts as its own guardian.

The key insight is that synchronization is the object's responsibility, not the caller's. Callers interact with a clean interface without worrying about concurrency.

Core Principles

  1. Encapsulation: Private data, public synchronized methods
  2. Mutual Exclusion: Only one thread executes inside the monitor at a time
  3. Condition Synchronization: Threads can wait for conditions and be notified

2. The Problem: External Locking is Error-Prone

Without the Monitor Object Pattern, synchronization becomes the caller's burden.

The External Locking Approach

Every caller must:

  1. Know which lock protects the data
  2. Acquire the lock before access
  3. Release the lock after access
  4. Handle lock correctly in all code paths

What Goes Wrong

Problem 1: Forgotten Locks

Problem 2: Wrong Lock

Problem 3: Lock Leaks

Problem 4: Inconsistent Operations

The Solution: Monitor Object

The monitor encapsulates synchronization. Callers never touch locks.

Callers just call methods. The monitor handles all locking internally.

3. How the Monitor Object Works

A monitor object has three components:

  1. Private state that needs protection
  2. Synchronized methods that access the state
  3. An implicit lock (one per object)

Execution Model

When a thread calls a synchronized method:

Only one thread executes inside the monitor at any time. Others wait at the door.

State Diagram

4. Basic Implementation

Let's build a thread-safe bank account using the Monitor Object Pattern.

4.1 Simple Monitor Object

The synchronized keyword makes each method acquire the object's intrinsic lock before executing.

4.2 Using the Monitor

Callers don't manage locks. The account handles everything.

4.3 Explicit Lock Version

For more control, use explicit locks instead of the synchronized keyword.

The try-finally pattern ensures the lock is always released.

5. Condition Variables: Wait and Notify

Sometimes a thread needs to wait for a condition before proceeding. The Monitor Object Pattern supports this through condition variables.

The Problem: Busy Waiting

This wastes CPU cycles and holds the lock, preventing deposits.

The Solution: Wait and Notify

Implementation with Wait/Notify

When a thread calls wait():

  1. It releases the monitor lock
  2. It goes to sleep
  3. When notified, it reacquires the lock and continues

Why Use while Instead of if?

Spurious wakeups can occur. The thread may wake up even when the condition isn't met. Always recheck in a loop.

Sequence Diagram

6. Multiple Conditions

Complex monitors often need multiple conditions. A thread might wait for different reasons.

Example: Bounded Buffer

A buffer that blocks when full (for producers) and when empty (for consumers).

Implementation with Multiple Conditions

Using separate conditions is more efficient. notFull.signal() only wakes producers, not consumers.

7. Monitor Object vs External Locking

Comparison

Scroll
Aspect
External Locking
Monitor Object

Lock management

Caller's job

Object's job

Error risk

High

Low

Flexibility

High

Moderate

Encapsulation

Poor

Strong

Composability

Difficult

Easier

When to Use Each

Use Monitor Object when:

  • Object has clear boundaries
  • All operations are well-defined
  • Simplicity is preferred

Use External Locking when:

  • Multiple objects need atomic access
  • Lock ordering is critical
  • Fine-grained control is needed

8. Common Pitfalls

Pitfall 1: Exposing Internal State

Pitfall 2: Calling External Methods While Holding Lock

This can cause deadlocks. The callback might try to acquire another lock that's waiting for this one.

Solution: Release the lock before calling external code, or design to avoid such callbacks.

Pitfall 3: Long Operations Inside Monitor

Pitfall 4: Forgetting notifyAll

Use notifyAll() unless you're certain only one thread should proceed.

Pitfall 5: Not Using Loop for Wait

Always use a while loop. Conditions may not hold after waking up.

9. Monitor Object in Different Languages

Java

Python

C++

Go

Go uses channels more than monitors, but you can build one:

10. When to Use in LLD Interviews

The Monitor Object Pattern appears in many interview scenarios:

Problem
Monitor Object Application

Design a Thread-Safe Counter

Counter object with synchronized increment/decrement

Design a Connection Pool

Pool object manages connections with wait/notify

Design a Rate Limiter

Limiter object tracks requests, blocks when exceeded

Design a Blocking Queue

Queue with synchronized put/take, wait when full/empty

Design a Thread-Safe Cache

Cache object with synchronized get/put operations

Design a Semaphore

Semaphore object with acquire/release

What to Mention in Interviews

  1. Pattern Recognition: "This object needs thread-safe access. I'll use the Monitor Object Pattern to encapsulate synchronization."
  2. Encapsulation Benefit: "Callers don't need to manage locks. The object guarantees thread safety internally."
  3. Wait/Notify: "When threads need to wait for conditions, I'll use wait() to release the lock and sleep, then notify() to wake waiters."
  4. Loop for Wait: "I always use a while loop around wait() to handle spurious wakeups."
  5. Lock Scope: "I'll keep synchronized sections minimal to maximize concurrency."
  6. Avoid Pitfalls: "I won't call external code while holding the lock to prevent deadlocks."

11. Summary

The Monitor Object Pattern encapsulates synchronization within an object, making thread safety the object's responsibility rather than the caller's.

Key Components

Component
Purpose

Private State

Data protected from direct access

Synchronized Methods

Entry points that acquire lock automatically

Intrinsic Lock

One per object, ensures mutual exclusion

Condition Variables

Enable threads to wait for conditions

Core Operations

Operation
Effect

synchronized

Acquires object lock before method execution

wait()

Releases lock and sleeps until notified

notify()

Wakes one waiting thread

notifyAll()

Wakes all waiting threads

Benefits

  • Encapsulates synchronization complexity
  • Reduces caller errors
  • Guarantees consistent locking
  • Supports condition synchronization

When to Use

  • Object has well-defined thread-safe operations
  • Multiple threads access shared state
  • Simplicity is preferred over fine-grained control
  • Operations need to wait for conditions

When NOT to Use

  • Operations span multiple objects
  • Fine-grained locking is required
  • Lock-free alternatives are available
  • Performance requires reader-writer distinction

The Monitor Object Pattern is fundamental to concurrent object-oriented design. It combines data encapsulation with synchronization, making it easier to write correct concurrent code.

References

Thank you for reading!

If you found it valuable, hit a like and consider subscribing for more such content every week.

If you have any questions or suggestions, leave a comment.

This post is public so feel free to share it.

Share

P.S. If you're enjoying this newsletter and want to get even more value, consider becoming a [paid subscriber](https://blog.algomaster.io/subscribe).

As a paid subscriber, you'll unlock all premium articles and gain full access to all [premium courses](https://algomaster.io/newsletter/paid/resources) on [algomaster.io](https://algomaster.io).

Unlock Full Access

There are [group discounts](https://blog.algomaster.io/subscribe?group=true), [gift options](https://blog.algomaster.io/subscribe?gift=true), and [referral bonuses](https://blog.algomaster.io/leaderboard) available.

Checkout my [Youtube channel](https://www.youtube.com/@ashishps_1/videos) for more in-depth content.

Follow me on [LinkedIn](https://www.linkedin.com/in/ashishps1/) and [X](https://twitter.com/ashishps_1) to stay updated.

Checkout my [GitHub repositories](https://github.com/ashishps1) for free interview preparation resources.

I hope you have a lovely day!

See you soon, Ashish