Tutorial

Java Lock Example - ReentrantLock

Published on August 3, 2022
author

Pankaj

Java Lock Example - ReentrantLock

Welcome to Java Lock example tutorial. Usually when working with multi-threaded environment, we use synchronized for thread safety.

Java Lock

java lock, java lock example, ReentrantLock in Java, java locks Most of the times, synchronized keyword is the way to go but it has some shortcomings that lead the way to inclusion of Lock API in Java Concurrency package. Java 1.5 Concurrency API came up with java.util.concurrent.locks package with Lock interface and some implementation classes to improve the Object locking mechanism. Some important interfaces and classes in Java Lock API are:

  1. Lock: This is the base interface for Lock API. It provides all the features of synchronized keyword with additional ways to create different Conditions for locking, providing timeout for thread to wait for lock. Some of the important methods are lock() to acquire the lock, unlock() to release the lock, tryLock() to wait for lock for a certain period of time, newCondition() to create the Condition etc.

  2. Condition: Condition objects are similar to Object wait-notify model with additional feature to create different sets of wait. A Condition object is always created by Lock object. Some of the important methods are await() that is similar to wait() and signal(), signalAll() that is similar to notify() and notifyAll() methods.

  3. ReadWriteLock: It contains a pair of associated locks, one for read-only operations and another one for writing. The read lock may be held simultaneously by multiple reader threads as long as there are no writer threads. The write lock is exclusive.

  4. ReentrantLock: This is the most widely used implementation class of Lock interface. This class implements the Lock interface in similar way as synchronized keyword. Apart from Lock interface implementation, ReentrantLock contains some utility methods to get the thread holding the lock, threads waiting to acquire the lock etc. synchronized block are reentrant in nature i.e if a thread has lock on the monitor object and if another synchronized block requires to have the lock on the same monitor object then thread can enter that code block. I think this is the reason for the class name to be ReentrantLock. Let’s understand this feature with a simple example.

    public class Test{
    
    public synchronized foo(){
        //do something
        bar();
      }
    
      public synchronized bar(){
        //do some more
      }
    }
    

    If a thread enters foo(), it has the lock on Test object, so when it tries to execute bar() method, the thread is allowed to execute bar() method since it’s already holding the lock on the Test object i.e same as synchronized(this).

Java Lock Example - ReentrantLock in Java

Now let’s see a simple example where we will replace synchronized keyword with Java Lock API. Let’s say we have a Resource class with some operation where we want it to be thread-safe and some methods where thread safety is not required.

package com.journaldev.threads.lock;

public class Resource {

	public void doSomething(){
		//do some operation, DB read, write etc
	}
	
	public void doLogging(){
		//logging, no need for thread safety
	}
}

Now let’s say we have a Runnable class where we will use Resource methods.

package com.journaldev.threads.lock;

public class SynchronizedLockExample implements Runnable{

	private Resource resource;
	
	public SynchronizedLockExample(Resource r){
		this.resource = r;
	}
	
	@Override
	public void run() {
		synchronized (resource) {
			resource.doSomething();
		}
		resource.doLogging();
	}
}

Notice that I am using synchronized block to acquire the lock on Resource object. We could have created a dummy object in the class and used that for locking purpose. Now let’s see how we can use java Lock API and rewrite above program without using synchronized keyword. We will use ReentrantLock in java.

package com.journaldev.threads.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConcurrencyLockExample implements Runnable{

	private Resource resource;
	private Lock lock;
	
	public ConcurrencyLockExample(Resource r){
		this.resource = r;
		this.lock = new ReentrantLock();
	}
	
	@Override
	public void run() {
		try {
			if(lock.tryLock(10, TimeUnit.SECONDS)){
			resource.doSomething();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}finally{
			//release lock
			lock.unlock();
		}
		resource.doLogging();
	}

}

As you can see that, I am using tryLock() method to make sure my thread waits only for definite time and if it’s not getting the lock on object, it’s just logging and exiting. Another important point to note is the use of try-finally block to make sure lock is released even if doSomething() method call throws any exception.

Java Lock vs synchronized

Based on above details and program, we can easily conclude following differences between Java Lock and synchronization.

  1. Java Lock API provides more visibility and options for locking, unlike synchronized where a thread might end up waiting indefinitely for the lock, we can use tryLock() to make sure thread waits for specific time only.
  2. Synchronization code is much cleaner and easy to maintain whereas with Lock we are forced to have try-finally block to make sure Lock is released even if some exception is thrown between lock() and unlock() method calls.
  3. synchronization blocks or methods can cover only one method whereas we can acquire the lock in one method and release it in another method with Lock API.
  4. synchronized keyword doesn’t provide fairness whereas we can set fairness to true while creating ReentrantLock object so that longest waiting thread gets the lock first.
  5. We can create different conditions for Lock and different thread can await() for different conditions.

That’s all for Java Lock example, ReentrantLock in java and a comparative analysis with synchronized keyword.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors
Default avatar
Pankaj

author

While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
JournalDev
DigitalOcean Employee
DigitalOcean Employee badge
November 9, 2020

Hi, can’t we able to lock two sync blocks with same object? method1() { synchronized(this) { } } method2() { synchronized(this) { } } I need both the sync blocks to be in sync Is it can be handled by Lock api?

- Karthickkumar. R

    JournalDev
    DigitalOcean Employee
    DigitalOcean Employee badge
    November 21, 2019

    Hi Pankaj, If a thread is not able to get the lock using trylock() and since we are already in try block wouldn’t the code in finally throw an exception as it is trying to unlock the lock which the thread does not own…

    - AS

      JournalDev
      DigitalOcean Employee
      DigitalOcean Employee badge
      February 13, 2019

      Hi, Cannot use ‘Copy’ to copy to clipboard. This is an issue for all the pages. I am using google chrome browser. Thanks.

      - Nimish

        JournalDev
        DigitalOcean Employee
        DigitalOcean Employee badge
        June 11, 2017

        Hi Pankaj, Very good explanation. I tried this example working fine. But I would like to high light one case which is not handle by this example. In finally block before releasing lock, we need to make sure Mock is held by current thread otherwise it will throw IllegalMonitorStateException. Attaching tested snippet code. finally{ System.out.println(Thread.currentThread().getName() + " tries to unlock"); if(Thread.holdsLock(resource)){ lock.unlock(); } } Best Regards.

        - Riteeka

          JournalDev
          DigitalOcean Employee
          DigitalOcean Employee badge
          November 22, 2016

          Hi Pankaj, I like your posts a lot, awesome posts, very clear and precise!!! I have a doubt on reentrant lock example above, what are you trying to explain here? wanna thread safe doSomthing() method, isn’t it? If so, synchronisation/lock logic shouldn’t be inside class of doSomething method instead inside run method of Thread? With above example how can you ensure the thread safety of doSomething() method? Thanks Ravi.

          - Ravi

            JournalDev
            DigitalOcean Employee
            DigitalOcean Employee badge
            September 9, 2016

            and where was the ReEntrantLock example? i see only synchronized vs Lock, not ReentrantLock

            - Gunatron

              JournalDev
              DigitalOcean Employee
              DigitalOcean Employee badge
              January 11, 2016

              please see below, also the the Runnable classes: public class SynchronizedExample implements Runnable{ private Resource resource; public SynchronizedExample(Resource r){ this.resource = r; } @Override public void run() { synchronized (resource) { resource.doSomething(); resource.doLogging(“synchronized hold released”); } } } public class LockExample implements Runnable{ private Resource resource; private Lock lock; public LockExample(Resource r){ this.resource = r; this.lock = new ReentrantLock(); } @Override public void run() { try { if(lock.tryLock(10, TimeUnit.SECONDS)){ resource.doSomething(); } } catch (InterruptedException e) { e.printStackTrace(); }finally{ //release lock resource.doLogging(“lock released”); lock.unlock(); } } }

              - Riley

                JournalDev
                DigitalOcean Employee
                DigitalOcean Employee badge
                January 11, 2016

                here’s the code i used based upon yours: public class Resource { public void doSomething(){ //do some operation, DB read, write etc try { System.out.println("resource locked by thread: " + Thread.currentThread().getName()) ; Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } } public void doLogging(String str){ //logging, no need for thread safety System.out.println(str) ; } } public class LockTest { public static void main(String[] args) throws InterruptedException { Resource res = new Resource() ; LockExample ex = new LockExample(res); Thread t1 = new Thread(ex, “t1”); t1.start(); Thread t2 = new Thread(ex, “t2”); t2.start(); t1.join(); t2.join(); System.out.println(“completed lock example”); } } public class SynchronizedTest { public static void main(String[] args) throws InterruptedException { Resource res = new Resource() ; SynchronizedExample ex = new SynchronizedExample(res); Thread t1 = new Thread(ex, “t1”); t1.start(); Thread t2 = new Thread(ex, “t2”); t2.start(); t1.join(); t2.join(); System.out.println(“completed synchronized example”); } }

                - Riley

                  JournalDev
                  DigitalOcean Employee
                  DigitalOcean Employee badge
                  December 29, 2015

                  when using lock.tryLock() one has to remember that lock.unlock() needs to be done in finally only when lock.tryLock() was true i.e the local was acquired . hence something like below should be done . public void run() { boolean b = false; if (Thread.currentThread().getName().equalsIgnoreCase(“second”)) { try { Thread.sleep(1200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(“in second thread”); } try { b = lock.tryLock(); System.out.println("b in " + Thread.currentThread().getName() + " thread is : " + b); if(b){ } } catch (Exception e) { e.printStackTrace(); }finally{ //release lock if (b) { lock.unlock(); } } }

                  - jitendra

                    JournalDev
                    DigitalOcean Employee
                    DigitalOcean Employee badge
                    November 22, 2015

                    Please, leave out all these fake comments. I think you spend more time commenting your own post that in actually writing.

                    - tELLtHEtRUTH

                      Try DigitalOcean for free

                      Click below to sign up and get $200 of credit to try our products over 60 days!

                      Sign up

                      Join the Tech Talk
                      Success! Thank you! Please check your email for further details.

                      Please complete your information!

                      Become a contributor for community

                      Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

                      DigitalOcean Documentation

                      Full documentation for every DigitalOcean product.

                      Resources for startups and SMBs

                      The Wave has everything you need to know about building a business, from raising funding to marketing your product.

                      Get our newsletter

                      Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

                      New accounts only. By submitting your email you agree to our Privacy Policy

                      The developer cloud

                      Scale up as you grow — whether you're running one virtual machine or ten thousand.

                      Get started for free

                      Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

                      *This promotional offer applies to new accounts only.