Thread Lock classes in Java

By Himanshu Sharma


Since now we have some idea on locks and how threads use them for execution, Let’s look into some classes which further helps us in our use cases.

ReentrantLock class:

In addition to lock functionality, this class provide few more utility methods which help us in our user cases.

tryLock() method: This method returns boolean value “true” or “false” depending on whether lock is available or not.

Refer the code below as example. When below code is executed, intermittently we get “Lock not available…” statements in console, indicating lock cannot be acquired by the thread –


  public class ReentrantLockExample {
  
    public static void main(String[] args) throws InterruptedException {
  
      SharedReentrantClass sharedObject = new SharedReentrantClass();
  
      Thread readThread = new Thread(() -> {
        while (true) {
          try {
            Thread.sleep(50);
            sharedObject.getLockObj().lock();
            System.out.println(System.currentTimeMillis() + "::" + sharedObject.getData() + "::");
          } catch (InterruptedException e) {
            throw new RuntimeException(e);
          } finally {
            sharedObject.getLockObj().unlock();
          }
        }
      });
  
      Thread updateThread = new Thread(() -> {
        while (true) {
          try {
            Thread.sleep(100);
            if (sharedObject.getLockObj().tryLock()) {
              sharedObject.setData(sharedObject.getData() + 1);
              sharedObject.getLockObj().unlock();
            } else {
              System.out.println("Lock not available...");
            }
          } catch (InterruptedException e) {
            throw new RuntimeException(e);
          }
        }
      });
  
      readThread.start();
      updateThread.start();
      readThread.join();
      updateThread.join();
      System.out.println("Finished....");
    }
  }
  
  class SharedReentrantClass {
    int data = 1;
    private Lock lockObj = new ReentrantLock();
  
    public int getData() {
      return data;
    }
  
    public void setData(int data) {
      this.data = data;
    }
  
    public Lock getLockObj() {
      return lockObj;
    }
  }
 

ReentrantReadWriteLock class:

This class provides two objects for locking – Read Lock and Write lock.

Refer below code and on executing, we can observe console output supporting above points.


  public class ReentrantRWLockExample {
  
    public static void main(String[] args) throws InterruptedException {
  
      SharedReentrantRWClass sharedObject = new SharedReentrantRWClass();
  
      Thread readThread1 = new Thread(() -> {
        while (true) {
          ReentrantReadWriteLock.ReadLock readLock = sharedObject.getRwLock().readLock();
          if (readLock.tryLock()) {
            System.out.println(Thread.currentThread().getName() + "::" + sharedObject.getData());
            readLock.unlock();
          } else {
            System.out.println("Did not get Lock since write in progress...");
          }
        }
      });
  
      Thread updateThread = new Thread(() -> {
        while (true) {
          try {
            if (sharedObject.getRwLock().writeLock().tryLock()) {
              Thread.sleep(100);
              sharedObject.setData(sharedObject.getData() + 1);
              sharedObject.getRwLock().writeLock().unlock();
            } else {
              System.out.println("Did not get Lock since read in progress...");
            }
          } catch (InterruptedException e) {
            throw new RuntimeException(e);
          }
        }
      });
  
      readThread1.start();
      updateThread.start();
      readThread1.join();
      updateThread.join();
      System.out.println("Finished....");
    }
  }
  
  class SharedReentrantRWClass {
    int data = 1;
    private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
  
    public ReentrantReadWriteLock getRwLock() {
      return rwLock;
    }
  
    public int getData() {
      return data;
    }
  
    public void setData(int data) {
      this.data = data;
    }
  }
 

Semaphore class:

Semaphore class provides a provision to only allow a fixed number (can be more than one) of thread to acquire resources. It works on the concept permits where only a certain number of permits are available to be taken up by threads.

Refer the below code example where we can see producer thread printing out number of available permits.


  public class SemaphoreExample {
  
    public static void main(String[] args) throws InterruptedException {
      SemaphoreSharedClass sharedClass = new SemaphoreSharedClass();
  
      Thread producer = new Thread(() -> {
        try {
          while (true) {
            System.out.println("Available permits :: " + sharedClass.getSemaphore().availablePermits());
            sharedClass.getSemaphore().acquire();
            Thread.sleep(1000);
            sharedClass.producer();
            sharedClass.getSemaphore().release();
          }
        } catch (InterruptedException e) {
          throw new RuntimeException(e);
        }
      });
  
      Thread consumer1 = new Thread(() -> {
        try {
          while (true) {
            sharedClass.getSemaphore().acquire();
            Thread.sleep(500);
            sharedClass.consumer(1);
            sharedClass.getSemaphore().release();
          }
        } catch (InterruptedException e) {
          throw new RuntimeException(e);
        }
      });
  
      Thread consumer2 = new Thread(() -> {
        try {
          while (true) {
            sharedClass.getSemaphore().acquire();
            Thread.sleep(500);
            sharedClass.consumer(2);
            sharedClass.getSemaphore().release();
          }
        } catch (InterruptedException e) {
          throw new RuntimeException(e);
        }
      });
  
      producer.start();
      consumer1.start();
      consumer2.start();
      producer.join();
      consumer1.join();
      consumer2.join();
      System.out.println("....");
    }
  }
  
  class SemaphoreSharedClass {
  
    Semaphore semaphore = new Semaphore(2);
  
    public Semaphore getSemaphore() {
      return semaphore;
    }
  
    public void producer() {
      System.out.println("Produced data");
    }
  
    public void consumer(int number) {
      System.out.println("Consumed data :: " + number);
    }
  }
 

Methods wait(), notify() and notifyAll():

Refer the below code.


  public class ThreadComm {
  
    public static void main(String[] args) throws InterruptedException {
  
      ThreadCommSharedClass sharedClass = new ThreadCommSharedClass();
  
      Thread thread1 = new Thread(() -> {
        sharedClass.methodA(1);
      });
  
      Thread thread3 = new Thread(() -> {
        sharedClass.methodA(3);
      });
  
      Thread thread2 = new Thread(() -> {
        try {
          Thread.sleep(5000);
        } catch (InterruptedException e) {
          throw new RuntimeException(e);
        }
        sharedClass.methodB();
      });
  
      thread1.start();
      thread2.start();
      thread3.start();
      thread1.join();
      thread2.join();
      thread3.join();
  
      System.out.println("Finished.....");
  
    }
  }
  
  class ThreadCommSharedClass {
  
    public synchronized void methodA(int number) {
      try {
        System.out.println("Method A started " + number);
        wait();
        System.out.println("Notification received....");
      } catch (InterruptedException e) {
        throw new RuntimeException(e);
      }
    }
  
    public synchronized void methodB() {
      System.out.println("Processing done....");
      notifyAll(); // replace with notify()
    }
  }
 

Try replacing notifyAll() with notify() and you will observe that program will not get terminated because we have two threads in waiting state but notify() method only tell one of the threads to start contesting, Leaving other thread in wait state.