Open In App

Difference Between Lock and Monitor in Java Concurrency

Last Updated : 18 Jan, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

Java Concurrency deals with concepts like Multithreading and other concurrent operations. To manage shared resources effectively, tools like Locks (Mutex) and Monitors are used to ensure thread synchronization and avoid race conditions. Locks represent a low-level synchronization mechanism and Monitors provide a higher-level abstraction to manage thread coordination efficiently.

Lock vs Monitor

Aspects

Lock (Mutex)

Monitor

Origin

It is an early synchronization tool used since the introduction of multithreading concepts.It came into existence with advancements in thread synchronization mechanisms.

Implementation

It is implemented as a flag or field to coordinate thread access to shared resources.Synchronization is implemented as part of an object’s behavior, typically using synchronized.

Critical Section

Handled explicitly in the thread itself using methods like lock() and unlock().Managed implicitly by the shared resource using synchronized blocks or methods.

Synchronization

Threads manage synchronization explicitly, implementing mutual exclusion and cooperation.The shared resource handles synchronization, ensuring mutual exclusion automatically.

Coupling

Loosely coupled mechanism, with threads independently managing access control.Tightly coupled mechanism, where the shared resource manages all synchronization responsibilities.

Performance

More prone to errors, especially when lock handling overlaps with thread time slices.Efficient for smaller thread pools, though complex inter-thread communication may reduce performance.

Thread-Management

Threads are managed by the operating system or explicitly in the program.Threads are queued and managed by the monitor within the shared resource.

Usage

Used less frequently in modern applications; explicit locks are implemented through Java’s Concurrency API.Widely used due to implicit synchronization and ease of implementation.

Note: As monitors themselves are implemented with the necessary support of locks, it is often said that they are not different but complementary in their existence.

Locks in Java

Locks provide explicit synchronization mechanisms for threads to access shared resources safely. The Java Concurrency API includes the Lock interface, which offers finer control compared to implicit locks provided by synchronized blocks or methods.

Below is the illustration which demonstrates the functioning of basic locks.

Java-Locks

Example: Below is an example of a Reentrant lock in Java.

Java
// Java program to demonstrate the use of locks 
// This program uses ReentrantLock to safely 
// access a shared resource 
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Geeks {

    // Shared resource accessed by multiple threads
    private static int sharedResource = 0;

    // ReentrantLock for thread synchronization
    private static final Lock lock = new ReentrantLock();

    public static void main(String[] args) {
      
        // Creating two threads to 
        // increment the shared resource
        Thread t1 = new Thread(new IncrementTask());
        Thread t2 = new Thread(new IncrementTask());

        // Start both threads
        t1.start();
        t2.start();

        try {
          
            // Wait for both threads to complete
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            System.out.println("Thread interrupted");
        }

        // Print final value of shared resource
        System.out.println("Final value of sharedResource: " 
                           + sharedResource);
    }

    // Task to increment the shared resource
    static class IncrementTask implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 1000; i++) {
              
                // Acquire the lock
                lock.lock(); 
                try {
                    sharedResource++;
                } finally {
                  
                    // Release the lock
                    lock.unlock(); 
                }
            }
        }
    }
}

Output
Final value of sharedResource: 2000

Explanation: The above program creates two threads T1 and T2 that increment a shared resource, ensuring thread safety by acquiring and releasing the lock before modifying the resource. Ensures that only one thread can modify the resource at a time. The program prints the final value of the shared resource after both threads finish execution.

Monitor in Java

Monitor in Java Concurrency is a synchronization mechanism that provides the fundamental requirements of multithreading namely mutual exclusion between various threads and cooperation among threads working at common tasks. Monitors basically ‘monitor’ the access control of shared resources and objects among threads. Using this construct only one thread at a time gets access control over the critical section at the resource while other threads are blocked and made to wait until certain conditions.

Example: The below code demonstrates how two threads t1 and t2 are synchronized to use a shared data object (Printer). 

Java
// Java Program to Illustrate Monitoe in Java Concurrency
import java.io.*;

class SharedDataPrinter
{

	// Monitor implementation is carried on by
	// Using synchronous method
	synchronized public void display(String str)
	{

		for (int i = 0; i < str.length(); i++)
        {
			System.out.print(str.charAt(i));

			// Try-catch block for exceptions
			// Because sleep() method is used
			try 
            {

			// Making thread to sleep for
			// nanoseconds as passed in the arguments
				Thread.sleep(100);
			}
			catch (Exception e) {
		 }
 	  }
   }
}

class Thread1 extends Thread {

	SharedDataPrinter p;

	// Thread
	public Thread1(SharedDataPrinter p)
	{

		// This keyword refers to current instance itself
		this.p = p;
	}

	// run() method for this thread invoked as
	// start() method is called in the main() method
	public void run()
	{

		// Print statement
		p.display("Geeks");
	}
}

// Class 2 (similar to class 1)
// Helper class extending the Thread class
class Thread2 extends Thread {

	SharedDataPrinter p;

	public Thread2(SharedDataPrinter p) { this.p = p; }

	public void run()
	{

		// Print statement
		p.display(" for Geeks");
	}
}

class Geeks
{
	public static void main(String[] args)
	{

		// Instance of a shared resource used to print
		// strings (single character at a time)
		SharedDataPrinter printer = new SharedDataPrinter();

		// Thread objects sharing data printer
		Thread1 t1 = new Thread1(printer);
		Thread2 t2 = new Thread2(printer);

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

Output:


Explanation: In the above example, display method in SharedDataPrinter is synchronized, and the allows only one thread to access and print characters at a time. Two threads, Thread1 and Thread2, share this SharedDataPrinter object. The synchronized keyword acts as a monitor, ensuring that the output from both threads is not interleaved that’s why we see the each character of string showing in a proper format one by one.



Next Article
Article Tags :
Practice Tags :

Similar Reads