In Java, what is the difference between a monitor and a lock

The doc https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html might not be a very good place to figure out the difference between Lock and Monitor, especially the terms it mentioned: intrinsic lock, monitor lock and simply monitor, which seems suggest that monitor and lock are interchangeable.

This is not true.

Monitor is a structure used for multi-thread synchronization. It consists of a lock and several condition variables. A condition variable is a queue that threads can put them on when a given condition is not as desired. Some other thread can wake these threads up when it makes the condition true. Condition Variable is a way helps threads cooperating with each other.

In simple synchronization cases, we only make use of the lock the monitor provided, like this example:

class SimpleCase {
  int counter;

  synchronized inc() int {
    return counter++;
  }
} 

Threads doing inc() needs no cooperation, only lock is needed to make the threads mutually exclusive, thus makes the counter thread safe.

While in more complicated cases, no only mutual exclusion(mutex) is needed, but also cooperation.

For example, the bounded consumer/producer problem: multi consumers and producers consume and send messages to a queue. Cooperation is needed cause the message queue has a max size, when the queue is full, no more messages can be sent, and when the queue is empty, no more messages can be consumed.

Below is the code showing the producer:

package monitor;

public class Producer {
    BoundedQueue queue;

    public Producer(BoundedQueue queue) {
        this.queue = queue;
    }

    public void send(int msg) throws InterruptedException {
        synchronized (queue) {
            // wait till there is room to produce
            while (queue.isFull()) {
                queue.wait();
            }

            // business logic here
            queue.add(msg);
            System.out.println("sent:" + msg + ", from:" + Thread.currentThread().getName());

            // before exit, call notify() to wake up waiting threads
            queue.notifyAll();
        }// implicit release the lock when exiting the synchronized block
    }
}

In the code, the BoundedQueue is used as a monitor, except for mutual exclusion, producers and consumers also need cooperation: when queue is full, producers need to wait(), and when queue has available slots, producers need to be notified to wake up from the wait, after producer send data to the queue, it also needs to call notifyAll() in case there are consumers waiting for the condition that the queue is not empty.

Here, the ability to wait and notify is provided by Monitor to make threads cooperate.

Hope this helps you understand the difference between Monitor and Lock.

Ref:

  • https://en.wikipedia.org/wiki/Monitor_(synchronization)
  • http://pages.cs.wisc.edu/~remzi/OSTEP/threads-cv.pdf

From the official documentation of Locks and Synchronization:

  • Synchronization is built around an internal entity known as the intrinsic lock or monitor lock. (The API specification often refers to this entity simply as a "monitor.")
  • Every object has an intrinsic lock associated with it. By convention, a thread has to acquire the object's monitor lock before accessing them, and then release the monitor lock when it's done with them. A thread is said to own the lock between the time it has acquired the lock and released the lock. As long as a thread owns a monitor lock, no other thread can acquire the same lock. The other thread will block when it attempts to acquire the lock.
  • When a thread releases the lock, a happens-before relationship is established between that action and any subsequent acquisition of the same lock.

So a monitor and a lock can not be compared for differences, rather they are complementary to each other. Every object in Java is associated with a monitor which a thread can lock or unlock.


Locks

A lock is kind of data which is logically part of an object’s header on the heap memory. Each object in a JVM has this lock (or mutex) that any program can use to coordinate multi-threaded access to the object. If any thread want to access instance variables of that object; then thread must “own” the object’s lock (set some flag in lock memory area). All other threads that attempt to access the object’s variables have to wait until the owning thread releases the object’s lock (unset the flag).

Once a thread owns a lock, it can request the same lock again multiple times, but then has to release the lock the same number of times before it is made available to other threads. If a thread requests a lock three times, for example, that thread will continue to own the lock until it has “released” it three times.

Please note that lock is acquired by a thread, when it explicitly ask for it. In Java, this is done with the synchronized keyword, or with wait and notify.

Monitors

Monitor is a synchronization construct that allows threads to have both mutual exclusion (using locks) and cooperation i.e. the ability to make threads wait for certain condition to be true (using wait-set).

In other words, along with data that implements a lock, every Java object is logically associated with data that implements a wait-set. Whereas locks help threads to work independently on shared data without interfering with one another, wait-sets help threads to cooperate with one another to work together towards a common goal e.g. all waiting threads will be moved to this wait-set and all will be notified once lock is released. This wait-set helps in building monitors with additional help of lock (mutex).

For more clarification refer -

UNDERSTANDING THREADS, MONITORS AND LOCKS

Difference between lock and monitor – Java Concurrency