Difference Between Lock and Monitor in Java Concurrency
Last Updated :
18 Jan, 2025
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.
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();
}
}
}
}
}
OutputFinal 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.