[java] Why use a ReentrantLock if one can use synchronized(this)?

I'm trying to understand what makes the lock in concurrency so important if one can use synchronized (this). In the dummy code below, I can do either:

  1. synchronized the entire method or synchronize the vulnerable area (synchronized(this){...})
  2. OR lock the vulnerable code area with a ReentrantLock.

Code:

    private final ReentrantLock lock = new ReentrantLock(); 
    private static List<Integer> ints;

    public Integer getResult(String name) { 
        .
        .
        .
        lock.lock();
        try {
            if (ints.size()==3) {
                ints=null;
                return -9;
            }   

            for (int x=0; x<ints.size(); x++) {
                System.out.println("["+name+"] "+x+"/"+ints.size()+". values >>>>"+ints.get(x));
            }

        } finally {
            lock.unlock();
        } 
        return random;
}

The answer is


From oracle documentation page about ReentrantLock:

A reentrant mutual exclusion Lock with the same basic behaviour and semantics as the implicit monitor lock accessed using synchronized methods and statements, but with extended capabilities.

  1. A ReentrantLock is owned by the thread last successfully locking, but not yet unlocking it. A thread invoking lock will return, successfully acquiring the lock, when the lock is not owned by another thread. The method will return immediately if the current thread already owns the lock.

  2. The constructor for this class accepts an optional fairness parameter. When set true, under contention, locks favor granting access to the longest-waiting thread. Otherwise this lock does not guarantee any particular access order.

ReentrantLock key features as per this article

  1. Ability to lock interruptibly.
  2. Ability to timeout while waiting for lock.
  3. Power to create fair lock.
  4. API to get list of waiting thread for lock.
  5. Flexibility to try for lock without blocking.

You can use ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock to further acquire control on granular locking on read and write operations.

Have a look at this article by Benjamen on usage of different type of ReentrantLocks


Lets assume this code is running in a thread:

private static ReentrantLock lock = new ReentrantLock();

void accessResource() {
    lock.lock();
    if( checkSomeCondition() ) {
        accessResource();
    }
    lock.unlock();
}

Because the thread owns the lock it will allow multiple calls to lock(), so it re-enter the lock. This can be achieved with a reference count so it doesn't has to acquire lock again.


One thing to keep in mind is :

The name 'ReentrantLock' gives out a wrong message about other locking mechanism that they are not re-entrant. This is not true. Lock acquired via 'synchronized' is also re-entrant in Java.

Key difference is that 'synchronized' uses intrinsic lock ( one that every Object has ) while Lock API doesn't.


You can use reentrant locks with a fairness policy or timeout to avoid thread starvation. You can apply a thread fairness policy. it will help avoid a thread waiting forever to get to your resources.

private final ReentrantLock lock = new ReentrantLock(true);
//the param true turns on the fairness policy. 

The "fairness policy" picks the next runnable thread to execute. It is based on priority, time since last run, blah blah

also, Synchronize can block indefinitely if it cant escape the block. Reentrantlock can have timeout set.


ReentrantReadWriteLock is a specialized lock whereas synchronized(this) is a general purpose lock. They are similar but not quite the same.

You are right in that you could use synchronized(this) instead of ReentrantReadWriteLock but the opposite is not always true.

If you'd like to better understand what makes ReentrantReadWriteLock special look up some information about producer-consumer thread synchronization.

In general you can remember that whole-method synchronization and general purpose synchronization (using the synchronized keyword) can be used in most applications without thinking too much about the semantics of the synchronization but if you need to squeeze performance out of your code you may need to explore other more fine-grained, or special-purpose synchronization mechanisms.

By the way, using synchronized(this) - and in general locking using a public class instance - can be problematic because it opens up your code to potential dead-locks because somebody else not knowingly might try to lock against your object somewhere else in the program.


I think the wait/notify/notifyAll methods don't belong on the Object class as it pollutes all objects with methods that are rarely used. They make much more sense on a dedicated Lock class. So from this point of view, perhaps it's better to use a tool that is explicitly designed for the job at hand - ie ReentrantLock.


Synchronized locks does not offer any mechanism of waiting queue in which after the execution of one thread any thread running in parallel can acquire the lock. Due to which the thread which is there in the system and running for a longer period of time never gets chance to access the shared resource thus leading to starvation.

Reentrant locks are very much flexible and has a fairness policy in which if a thread is waiting for a longer time and after the completion of the currently executing thread we can make sure that the longer waiting thread gets the chance of accessing the shared resource hereby decreasing the throughput of the system and making it more time consuming.


Examples related to java

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How much should a function trust another function How to implement a simple scenario the OO way Two constructors How do I get some variable from another class in Java? this in equals method How to split a string in two and store it in a field How to do perspective fixing? String index out of range: 4 My eclipse won't open, i download the bundle pack it keeps saying error log

Examples related to multithreading

How can compare-and-swap be used for a wait-free mutual exclusion for any shared data structure? Waiting until the task finishes What is the difference between Task.Run() and Task.Factory.StartNew() Why is setState in reactjs Async instead of Sync? What exactly is std::atomic? Calling async method on button click WAITING at sun.misc.Unsafe.park(Native Method) How to use background thread in swift? What is the use of static synchronized method in java? Locking pattern for proper use of .NET MemoryCache

Examples related to concurrency

WAITING at sun.misc.Unsafe.park(Native Method) What is the Swift equivalent to Objective-C's "@synchronized"? Custom thread pool in Java 8 parallel stream How to check if another instance of my shell script is running How to use the CancellationToken property? What's the difference between a Future and a Promise? Why use a ReentrantLock if one can use synchronized(this)? NSOperation vs Grand Central Dispatch What's the difference between Thread start() and Runnable run() multiprocessing.Pool: When to use apply, apply_async or map?

Examples related to synchronize

Why use a ReentrantLock if one can use synchronized(this)?

Examples related to reentrantlock

Why use a ReentrantLock if one can use synchronized(this)?