← Back to Multithreading Course

Class 15: Understanding Locks and Object Monitors

Understand how Java objects act as locks and how monitors help coordinate access between threads.

1. Why Do We Need Locks?

In our previous classes, we learned about race conditions and how synchronization helps prevent them.

Imagine two threads trying to update the same bank account balance at the same time.

If both threads read and update the balance without proper coordination, one update might overwrite the other.

We need a way to ensure that only one thread performs a protected operation at a time.

This is where locks come into the picture.

2. What Is a Lock?

A lock is a mechanism that controls access to a shared resource.

Think about a meeting room that can be used by only one person at a time.

When someone enters the room, they take the key. Anyone else who wants to use the room must wait until the key is returned.

A lock works in a similar way.

  • A thread acquires the lock before entering a protected section.
  • Another thread trying to acquire the same lock must wait.
  • When the first thread releases the lock, another waiting thread can acquire it.

Java uses locks to coordinate access to shared data.

3. What Is an Object Monitor?

Every Java object has an associated monitor.

A monitor is a mechanism that Java uses to coordinate threads accessing synchronized code associated with that object.

You can think of the monitor as the mechanism behind the object's synchronization lock.

When a thread enters a synchronized instance method, it must acquire the monitor lock of that object.

For example:

class Counter {

    int count = 0;

    synchronized void increment() {
        count++;
    }
}

When a thread calls increment(), it acquires the lock associated with the Counter object.

If another thread tries to call a synchronized method using the same object, it must wait until that lock becomes available.

4. Understanding the Lock with Two Threads

Let's look at a simple example.

class SharedResource {

    synchronized void useResource() {

        System.out.println(
            Thread.currentThread().getName()
            + " entered the method"
        );

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        System.out.println(
            Thread.currentThread().getName()
            + " leaving the method"
        );
    }
}

public class Main {

    public static void main(String[] args) {

        SharedResource resource = new SharedResource();

        Thread t1 = new Thread(
            () -> resource.useResource(),
            "Thread-1"
        );

        Thread t2 = new Thread(
            () -> resource.useResource(),
            "Thread-2"
        );

        t1.start();
        t2.start();
    }
}

Both threads are trying to call the same synchronized method on the same SharedResource object.

One possible execution is:

Thread-1 entered the method
Thread-1 leaving the method
Thread-2 entered the method
Thread-2 leaving the method

Thread-1 acquires the object's lock and enters the method. Thread-2 must wait because the same lock is already held.

Once Thread-1 leaves the method, it releases the lock. Thread-2 can then acquire it and enter the method.

The actual order of the two threads is not guaranteed. Thread-2 could acquire the lock first instead.

5. What Happens When a Thread Is Sleeping Inside a Synchronized Method?

Look carefully at the previous example.

We used Thread.sleep(2000) inside the synchronized method.

Does the sleeping thread release the object's lock?

No.

This is an important point.

Calling sleep() pauses the current thread, but it does not release the lock that the thread holds.

So, while Thread-1 is sleeping inside the synchronized method, Thread-2 still cannot enter another synchronized method using the same object.

We will learn more about wait() and how it behaves differently in a later class.

6. Can Multiple Synchronized Methods Use the Same Monitor?

Yes.

Consider this class:

class BankAccount {

    private int balance = 1000;

    synchronized void deposit(int amount) {
        balance += amount;
    }

    synchronized void withdraw(int amount) {
        balance -= amount;
    }
}

Both methods are synchronized instance methods.

They use the same object's monitor lock when called on the same BankAccount object.

So, if one thread is executing deposit(), another thread cannot simultaneously enter withdraw() on that same object.

This helps us coordinate access to the shared balance.

7. Important Things to Understand

  • Every Java object has an associated monitor.
  • A thread must acquire the object's monitor lock before entering a synchronized instance method or block using that object.
  • Only one thread at a time can hold a particular object's monitor lock.
  • Other threads trying to acquire the same lock must wait.
  • When a thread leaves synchronized code, it releases the lock.
  • Thread.sleep() does not release a lock held by the sleeping thread.

Remember: An object's monitor helps Java coordinate threads that need to execute synchronized code using that object.

Test Your Understanding

Let's see how well you understood this class!

Question 1: Why do we use locks in multithreaded applications?

Question 2: What is associated with every Java object?

Question 3: Two threads call synchronized methods on the same object. What happens?

Question 4: Does Thread.sleep() release a lock held by the current thread?

Question 5: When does a thread release the monitor lock after entering a synchronized method?

Class Summary

Let's quickly revise what we learned in this class.

  • A lock controls access to a shared resource.
  • Every Java object has an associated monitor.
  • Synchronized methods and blocks use an object's monitor lock.
  • Only one thread at a time can hold a particular object's monitor lock.
  • Multiple synchronized instance methods on the same object use the same lock.
  • sleep() does not release a lock.

Remember: A monitor helps coordinate threads, and a thread must acquire the associated lock before entering synchronized code.

What's Next?

Class 16: Instance Locks and Class Locks

We have learned that synchronized instance methods use the lock associated with an object.

But what happens when we use a static synchronized method? Does it use an individual object's lock or something else?

In the next class, we will understand the difference between instance locks and class locks.

See you in the next class!

0% Completed

You have completed 15 out of 46 classes.