Locks

⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 5 min

TL;DR

  • The java.util.concurrent.locks.Lock interface is an advanced alternative to the synchronized keyword.
  • It provides more granular and flexible control over thread synchronization.
  • It requires explicit lock() and unlock() calls.

Concept

The synchronized keyword uses “intrinsic locks”. While easy to use, they are rigid:

  1. They have no timeout functionality (if a thread waits for a lock, it waits forever).
  2. The lock must be acquired and released entirely within the same method/block.
  3. You cannot check if a lock is currently available before attempting to acquire it.

Java 5 introduced the Lock interface to solve these issues. It gives developers manual control over the lock lifecycle. You can try to acquire a lock for exactly 2 seconds, and if it fails, gracefully back out and do something else (using tryLock()).

Examples

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

class Resource {
    // Instantiate an explicit Lock object
    private final Lock lock = new ReentrantLock();
    
    public void accessResource() {
        
        lock.lock(); // Explicitly acquire the lock
        try {
            // CRITICAL SECTION
            System.out.println(Thread.currentThread().getName() + " is inside.");
            Thread.sleep(1000); 
            
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // MUST ALWAYS unlock in a finally block!
            // If the critical section throws an exception and you don't unlock,
            // the application will permanently deadlock.
            lock.unlock(); 
        }
    }
}

Interview Questions

Q: What is tryLock()?
A: tryLock() is a method on the Lock interface that attempts to acquire the lock immediately. If the lock is available, it acquires it and returns true. If the lock is held by another thread, it immediately returns false (or waits for a specified timeout) instead of freezing and waiting forever like synchronized does. This allows the thread to perform alternative tasks if the resource is busy.

Q: Why must you always call unlock() inside a finally block?
A: If an exception is thrown during the execution of your critical section, the code immediately breaks and jumps out of the try block. If lock.unlock() is just sitting at the bottom of the try block, it will be skipped. This means the thread crashes while still holding the lock. Every other thread in the application waiting for that lock will wait forever, causing a permanent deadlock. The finally block guarantees the lock is released no matter what happens.