How to Use Locks in Multi-Threaded Java Program?
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:
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 )); } } |
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
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.
Contact Us