Open In App

How to Use Locks in Multi-Threaded Java Program?

Last Updated : 02 Feb, 2021
Improve
Improve
Like Article
Like
Save
Share
Report

A lock may be a more flexible and complicated thread synchronization mechanism than the standard synchronized block. A lock may be a tool for controlling access to a shared resource by multiple threads. Commonly, a lock provides exclusive access to a shared resource: just one thread at a time can acquire the lock and everyone accesses to the shared resource requires that the lock be acquired first. However, some locks may allow concurrent access to a shared resource, like the read lock of a ReadWriteLock.

// Example of lock interface
Lock lock = new ReentrantLock();
lock.lock();
 
// critical section 
lock.unlock();

Methods in the lock interface

There are certain methods in a lock interface. We are gonna look at those along with their modifiers:

MODIFIERS DESCRIPTION
void lock() – It acquires the lock if it’s available; if the lock isn’t available a thread gets blocked until the lock is released
lockInterruptibly() – It is similar to lock() but it acquires the lock unless the thread is interrupted
unlock() – As the name suggests it simply releases the lock instance
condition newCondition() – It simply returns the new condition instance
boolean tryLock() – It attempts to accumulate the lock immediately, return true if locking succeeds
tryLock(long time, TimeUnit unit) – It is often almost like tryLock(), except it waits up the given timeout before abandoning trying to accumulate the Lock

Implementation of locks

Let’s see how can we implement some locks in Java:

1.readWriteLock()

ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

readWriteLock.readLock().lock();

    // ....
    
    
                ......//

readWriteLock.readLock().unlock();


readWriteLock.writeLock().lock();

    // only one writer can enter this section,
    // and only if no threads are currently reading.

readWriteLock.writeLock().unlock();

Below is the implementation of readWriteLock() method:

Java




// Implementation of ReadWriteLock in Java
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class GFG<O> {
  
    private final ReadWriteLock readWriteLock
        = new ReentrantReadWriteLock();
    private final Lock writeLock
        = readWriteLock.writeLock();
    private final Lock readLock = readWriteLock.readLock();
    private final List<O> list = new ArrayList<>();
  
    // setElement function sets
    // i.e., write the element to the thread
    public void setElement(O o)
    {
        // acquire the thread for writing
        writeLock.lock();
        try {
            list.add(o);
            System.out.println(
                "Element by thread "
                + Thread.currentThread().getName()
                + " is added");
        }
        finally {
            // To unlock the acquired write thread
            writeLock.unlock();
        }
    }
  
    // getElement function prints
    // i.e., read the element from the thread
    public O getElement(int i)
    {
        // acquire the thread for reading
        readLock.lock();
        try {
            System.out.println(
                "Elements by thread "
                + Thread.currentThread().getName()
                + " is printed");
            return list.get(i);
        }
        finally {
            // To unlock the acquired read thread
            readLock.unlock();
        }
    }
    public static void main(String[] args)
    {
        GFG<String> gfg = new GFG<>();
  
        gfg.setElement("Hi");
        gfg.setElement("Hey");
        gfg.setElement("Hello");
  
        System.out.println("Printing the last element : "
                           + gfg.getElement(2));
    }
}


Output

Element by thread main is added
Element by thread main is added
Element by thread main is added
Elements by thread main is printed
Printing the last element : Hello

2. reentrantLock()

public class lockImplement {
    //...
    ReentrantLock lock = new ReentrantLock();
    int counter = 0;

    public void testing() {
        lock.lock();
        try {
            // Critical section here
            count++;
        } finally {
            lock.unlock();
        }
    }
    //...
}

Below is the implementation of reentrantLock() method:

Java




// Java code to illustrate Reentrant Locks
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;
  
class worker implements Runnable {
    String name;
    ReentrantLock re;
    public worker(ReentrantLock rl, String n)
    {
        re = rl;
        name = n;
    }
    public void run()
    {
        boolean done = false;
        while (!done) {
            // Getting Outer Lock
            boolean ans = re.tryLock();
  
            // Returns True if lock is free
            if (ans) {
                try {
                    Date d = new Date();
                    SimpleDateFormat ft
                        = new SimpleDateFormat("hh:mm:ss");
                    System.out.println(
                        "task name - " + name
                        + " outer lock acquired at "
                        + ft.format(d)
                        + " Doing outer work");
                    Thread.sleep(1500);
  
                    // Getting Inner Lock
                    re.lock();
                    try {
                        d = new Date();
                        ft = new SimpleDateFormat(
                            "hh:mm:ss");
                        System.out.println(
                            "task name - " + name
                            + " inner lock acquired at "
                            + ft.format(d)
                            + " Doing inner work");
                        System.out.println(
                            "Lock Hold Count - "
                            + re.getHoldCount());
                        Thread.sleep(1500);
                    }
                    catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    finally {
                        // Inner lock release
                        System.out.println(
                            "task name - " + name
                            + " releasing inner lock");
  
                        re.unlock();
                    }
                    System.out.println("Lock Hold Count - "
                                       + re.getHoldCount());
                    System.out.println("task name - " + name
                                       + " work done");
  
                    done = true;
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
                finally {
                    // Outer lock release
                    System.out.println(
                        "task name - " + name
                        + " releasing outer lock");
  
                    re.unlock();
                    System.out.println("Lock Hold Count - "
                                       + re.getHoldCount());
                }
            }
            else {
                System.out.println("task name - " + name
                                   + " waiting for lock");
                try {
                    Thread.sleep(1000);
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  
public class test {
    static final int MAX_T = 2;
    public static void main(String[] args)
    {
        ReentrantLock rel = new ReentrantLock();
        ExecutorService pool
            = Executors.newFixedThreadPool(MAX_T);
        Runnable w1 = new worker(rel, "Job1");
        Runnable w2 = new worker(rel, "Job2");
        Runnable w3 = new worker(rel, "Job3");
        Runnable w4 = new worker(rel, "Job4");
        pool.execute(w1);
        pool.execute(w2);
        pool.execute(w3);
        pool.execute(w4);
        pool.shutdown();
    }
}


Output:

task name - Job2 waiting for lock
task name - Job1 outer lock acquired at 09:49:42 Doing outer work
task name - Job2 waiting for lock
task name - Job1 inner lock acquired at 09:49:44 Doing inner work
Lock Hold Count - 2
task name - Job2 waiting for lock
task name - Job2 waiting for lock
task name - Job1 releasing inner lock
Lock Hold Count - 1
task name - Job1 work done
task name - Job1 releasing outer lock
Lock Hold Count - 0
task name - Job3 outer lock acquired at 09:49:45 Doing outer work
task name - Job2 waiting for lock
task name - Job3 inner lock acquired at 09:49:47 Doing inner work
Lock Hold Count - 2
task name - Job2 waiting for lock
task name - Job2 waiting for lock
task name - Job3 releasing inner lock
Lock Hold Count - 1
task name - Job3 work done
task name - Job3 releasing outer lock
Lock Hold Count - 0
task name - Job4 outer lock acquired at 09:49:48 Doing outer work
task name - Job2 waiting for lock
task name - Job4 inner lock acquired at 09:49:50 Doing inner work
Lock Hold Count - 2
task name - Job2 waiting for lock
task name - Job2 waiting for lock
task name - Job4 releasing inner lock
Lock Hold Count - 1
task name - Job4 work done
task name - Job4 releasing outer lock
Lock Hold Count - 0
task name - Job2 outer lock acquired at 09:49:52 Doing outer work
task name - Job2 inner lock acquired at 09:49:53 Doing inner work
Lock Hold Count - 2
task name - Job2 releasing inner lock
Lock Hold Count - 1
task name - Job2 work done
task name - Job2 releasing outer lock
Lock Hold Count - 0

Note: The program might not work on an online IDE because of sleep call.



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads