The Lock interface provides more flexible and sophisticated locking operations than intrinsic locks (synchronized). It’s part of java.util.concurrent.locks and serves as the foundation for ReentrantLock and ReentrantReadWriteLock.
Code example comparing synchronized vs Lock
// Using synchronized (intrinsic lock)class Counter { private int count = 0; public synchronized void increment() { count++; }}// Using Lock interfaceclass Counter { private int count = 0; private Lock lock = new ReentrantLock(); public void increment() { lock.lock(); try { count++; } finally { lock.unlock(); // Must manually unlock } }}
Core Methods
lock()
Acquires the lock, blocking indefinitely until available.
If the lock is held by another thread, the current thread enters the waiting state and joins the lock’s Entry queue.
Ignores Interrupts
lock.lock(); // blocks until acquired, ignoring interruptstry { // critical section} finally { lock.unlock(); // Always unlock in finally block}
lockInterruptibly()
Acquires the lock but can be interrupted while waiting.
Throws InterruptedException if the thread is interrupted.
try { lock.lockInterruptibly(); // can throw while waiting try { // critical section if (Thread.interrupted()){ // manually throw while running throw new InterruptedException(); } // critical section // ... } finally { lock.unlock(); }} catch (InterruptedException e) { // Handle interruption Thread.currentThread().interrupt();}
Use case: When you want threads to be responsive to cancellation requests.
tryLock()
Attempts to acquire the lock immediately without blocking.
Returns true if successful, false if lock is held by another thread.
if (lock.tryLock()) { try { // critical section } finally { lock.unlock(); }} else { // Lock not available, do something else System.out.println("Could not acquire lock, skipping operation");}
Important!: tryLock() LockDoes not honour fairness settings, it will grab the lock immediately if available, even if other threads are waiting.
tryLock(long time, TimeUnit unit)
Unlike tryLock(), this variant is interruptible & thus the InterruptedException has to be handled.
Attempts to acquire the lock, waiting up to the specified timeout.
Returns true if acquired within the timeout, false otherwise.
Can be interrupted while waiting.
Use case: Deadlock prevention, timeout-based recovery strategies.
Example of deadlock prevention Always prefer lock ordering, but in cases where lock ordering is not possible, tryLock(time, unit) can help prevent deadlocks
The code bellow is a case where lock ordering is not possible
public void transfer(Account from, Account to, double amount) throws InterruptedException { while (true) { if (!from.lock.tryLock(50, TimeUnit.MILLISECONDS)) { Thread.sleep(random.nextInt(10)); // backoff continue; } boolean fromLockHeld = true; // Track if we still hold the lock try { if (!to.lock.tryLock(50, TimeUnit.MILLISECONDS)) { from.lock.unlock(); // Release immediately fromLockHeld = false; // Mark as released // fromLockHeld is needed so that we can release // the from lock before going into backoff. // This is to ensure liveliness and not hold on // to the lock for no reason. Thread.sleep(random.nextInt(10)); continue; } try { from.balance -= amount; to.balance += amount; return; } finally { to.lock.unlock(); } } finally { if (fromLockHeld) { // Only unlock if we still hold it from.lock.unlock(); } } }}
unlock()
Calling unlock() without holding the lock throws IllegalMonitorStateException
Releases the lock. Must be called by the thread that acquired the lock.
Should always be in a finally block to ensure release even if exceptions occur.
Threads waiting for Lock implementations are in WAITING or TIMED_WAITING states (not BLOCKED).
The BLOCKED state is reserved for intrinsic locks (synchronized).