← Back to Advanced Java Course | Chapter 11: Advanced & Security | Lesson 9 of 19

Java Locks & Semaphores

Locks and semaphores control how many threads can use something at once, like a key for a single room or tickets for a limited number of seats. They keep threads from clashing.
Syntax
java
Lock lock = new ReentrantLock();
lock.lock();
try {
    // critical section
} finally {
    lock.unlock();
}

ReentrantLock Basics

ReentrantLock is a more flexible, explicit alternative to the synchronized keyword. You must call lock() to acquire it and unlock() inside a finally block to release it, since unlike synchronized blocks, the JVM won't release a ReentrantLock for you automatically if an exception is thrown.

Example: ReentrantLock Basics

java
import java.util.concurrent.locks.ReentrantLock;
public class Main {
	static final ReentrantLock lock = new ReentrantLock();
	public static void main(String[] args) {
		lock.lock();
		try {
			System.out.println("Critical section");
		} finally {
			lock.unlock(); // must release manually, unlike synchronized
		}
	}
}

Non-Blocking Locks with tryLock()

tryLock() attempts to acquire a lock without blocking the calling thread -- it returns true immediately if the lock was free, or false right away if it wasn't, letting a thread decide to do something else instead of waiting indefinitely for a busy resource.

Example: Non-Blocking Locks with tryLock()

java
import java.util.concurrent.locks.ReentrantLock;
public class Main {
	static final ReentrantLock lock = new ReentrantLock();
	public static void main(String[] args) {
		if (lock.tryLock()) { // returns immediately instead of blocking
			try { System.out.println("Acquired the lock"); } finally { lock.unlock(); }
		} else {
			System.out.println("Lock was busy, doing something else");
		}
	}
}

ReadWriteLock

ReadWriteLock improves throughput by allowing any number of threads to read a shared resource simultaneously, while still guaranteeing that only one thread can hold the write lock at a time and that writes are exclusive of all reads -- ideal for data that's read far more often than it's modified.

Example: ReadWriteLock

java
import java.util.concurrent.locks.*;
public class Main {
	static final ReadWriteLock rwLock = new ReentrantReadWriteLock();
	public static void main(String[] args) {
		rwLock.readLock().lock(); // multiple readers allowed at once
		try { System.out.println("Reading"); } finally { rwLock.readLock().unlock(); }
		rwLock.writeLock().lock(); // exclusive of all reads and other writes
		try { System.out.println("Writing"); } finally { rwLock.writeLock().unlock(); }
	}
}

Semaphore Basics

A Semaphore controls access to a limited pool of a resource using permits: threads call acquire() to take a permit before proceeding and release() to give it back afterward, which is the standard way to cap how many threads can use something concurrently, like a fixed-size connection pool.

Example: Semaphore Basics

java
import java.util.concurrent.Semaphore;
public class Main {
	public static void main(String[] args) throws InterruptedException {
		Semaphore semaphore = new Semaphore(2); // 2 permits, like a connection pool of size 2
		semaphore.acquire();
		System.out.println("Permit acquired, available: " + semaphore.availablePermits());
		semaphore.release();
	}
}

Fair Lock Queueing

By default, Java locks make no guarantee about the order in which waiting threads acquire a contested lock. Enabling fairness in a ReentrantLock's constructor forces the lock to grant access to whichever thread has been waiting longest, at some cost to raw throughput.

Example: Fair Lock Queueing

java
import java.util.concurrent.locks.ReentrantLock;
public class Main {
	public static void main(String[] args) {
		ReentrantLock fairLock = new ReentrantLock(true); // grants access in waiting order
		fairLock.lock();
		try { System.out.println("Longest-waiting thread goes first"); } finally { fairLock.unlock(); }
	}
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Not calling unlock() in a finally block, so an exception leaves the lock held and other threads wait forever.
  2. Ignoring the boolean returned by tryLock() and running the protected code even when the lock was not obtained.
  3. Calling acquire() on a Semaphore and not calling release() in finally, so permits leak and threads block.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.