Java

Comparing ReentrantLock and Synchronized in Java

Comparing ReentrantLock and Synchronized in Java

In Java, both ReentrantLock and the synchronized keyword are used to achieve thread synchronization and prevent race conditions. However, they differ in their approach, features, and behavior. This table highlights the key differences between ReentrantLock and Synchronized:

Sr. No.KeyReentrantLockSynchronized
1Acquire LockReentrantLock provides the lock() method to acquire a lock on the shared resource by a thread.You need to use the synchronized keyword to acquire a lock.
2Release LockTo release a lock, programmers must explicitly call the unlock() method.The lock is released implicitly when the synchronized block or method exits.
3Ability to Interrupt LockThe lockInterruptibly() method can be used to interrupt a thread waiting for a lock.There is no direct way to interrupt a thread waiting for a lock when using the synchronized keyword.
4FairnessThe constructor of ReentrantLock has a fairness parameter. If set to true, locks favor granting access to the longest-waiting thread.The synchronized keyword does not guarantee any particular access order.
5Lock Release OrderLocks acquired by a ReentrantLock can be released in any order.Locks acquired using the synchronized keyword must be released in the reverse order of acquisition.
6PerformanceReentrantLock may have slightly better performance in situations with high contention due to its ability to back off and retry.The synchronized keyword may perform slightly better in situations with low contention.
7ScalabilityReentrantLock can potentially provide better scalability in some cases due to its advanced features like fairness and lock polling.The synchronized keyword can be less scalable in certain scenarios due to its lack of advanced features.
8FlexibilityReentrantLock provides more flexibility and advanced features, such as timed locks, interruptible locks, and fair/non-fair acquisition modes.The synchronized keyword is more straightforward but less flexible.

Examples:

1. Synchronized Example:

Java
public class SynchronizedExample {
    private static int counter = 0;

    public static synchronized void increment() {
        counter++;
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                increment();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                increment();
            }
        });

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

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Counter: " + counter); // Output: Counter: 20000
    }
}

2. ReentrantLock Example:

Java
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
    private static int counter = 0;
    private static ReentrantLock lock = new ReentrantLock();

    public static void increment() {
        lock.lock();
        try {
            counter++;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                increment();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                increment();
            }
        });

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

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Counter: " + counter); // Output: Counter: 20000
    }
}

In both examples, we have a shared counter variable that is incremented by two threads concurrently. In the first example, we use the synchronized keyword to ensure thread-safe access to the increment() method. In the second example, we use a ReentrantLock to achieve the same thread safety.

The output of both examples is Counter: 20000, which is the expected result after incrementing the counter 10,000 times by each thread.

While the synchronized keyword provides a straightforward way to achieve thread synchronization, ReentrantLock offers more advanced features and flexibility, such as fairness, lock polling, and the ability to interrupt a thread waiting for a lock. However, the choice between the two depends on the specific requirements of your application and the level of control and flexibility you need.

Avatar

Neelabh

About Author

As Neelabh Singh, I am a Senior Software Engineer with 6.6 years of experience, specializing in Java technologies, Microservices, AWS, Algorithms, and Data Structures. I am also a technology blogger and an active participant in several online coding communities.

You may also like

Blog Java Java 8 Features

Avoid NullPointerException in Java with Optional: A Comprehensive Guide

Are you tired of dealing with avoid NullPointerExceptions(NPEs) in your Java code? Look no further than the Optional class introduced
Blog Java Java 8 Features

Unlocking the Power of Java 8 Streams: A Guide to Efficient Data Processing

What are Java 8 Streams? At its core, a stream in Java 8 is a sequence of elements that facilitates