Mastering Java Locks: Reentrant, Synchronized, ReadWrite, and Spin Locks Explained
This article provides a comprehensive guide to Java locking mechanisms, covering ReentrantLock, synchronized, ReadWriteLock, and spin locks, with detailed code examples, performance considerations, common use cases, and best practices to ensure thread safety, avoid deadlocks, and optimize concurrency in multithreaded applications.
Introduction
Multithreaded programming is essential for leveraging multi‑core processors, but it introduces challenges around thread‑safe access to shared resources. Locks are the core mechanism for coordinating such access in Java.
Reentrant Lock (ReentrantLock)
A ReentrantLock allows the same thread to acquire the lock multiple times without deadlocking, offering flexibility over the built‑in synchronized construct. Typical usage requires explicit lock and unlock calls, usually wrapped in a try‑finally block.
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private int count = 0;
private ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock(); // acquire lock
try {
count++;
} finally {
lock.unlock(); // release lock
}
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}Mutex and synchronized Keyword
The synchronized keyword provides an implicit monitor lock for methods or code blocks, ensuring exclusive access without explicit lock management. It is simpler but less flexible than ReentrantLock.
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}ReadWrite Lock
ReadWriteLock (implemented by ReentrantReadWriteLock) permits multiple concurrent readers while allowing only one writer, improving performance when reads dominate writes.
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class SharedResource {
private int data = 0;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
public int readData() {
lock.readLock().lock();
try {
return data;
} finally {
lock.readLock().unlock();
}
}
public void writeData(int newValue) {
lock.writeLock().lock();
try {
data = newValue;
} finally {
lock.writeLock().unlock();
}
}
}Spin Lock
A spin lock repeatedly checks a flag until it becomes available, avoiding thread sleep/wakeup overhead. It is suitable for very short critical sections on low‑contention workloads.
import java.util.concurrent.atomic.AtomicBoolean;
public class SpinLock {
private AtomicBoolean locked = new AtomicBoolean(false);
public void lock() {
while (!locked.compareAndSet(false, true)) {
// spin waiting for lock release
}
}
public void unlock() {
locked.set(false);
}
}Lock Performance and Scalability
Choosing the right lock type balances performance and scalability. Over‑locking can degrade throughput, while inappropriate lock choice can cause contention and bottlenecks. Benchmarking different locks under realistic workloads helps identify the optimal configuration.
Common Lock Use Cases
1. Multithreaded Data Access
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SharedDataAccess {
private int sharedData = 0;
private Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try { sharedData++; } finally { lock.unlock(); }
}
public int getSharedData() {
lock.lock();
try { return sharedData; } finally { lock.unlock(); }
}
}2. Cache Management
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class CacheManager<K, V> {
private Map<K, V> cache = new HashMap<>();
private Lock lock = new ReentrantLock();
public void put(K key, V value) {
lock.lock();
try { cache.put(key, value); } finally { lock.unlock(); }
}
public V get(K key) {
lock.lock();
try { return cache.get(key); } finally { lock.unlock(); }
}
}3. Task Scheduling
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TaskScheduler {
private Lock lock = new ReentrantLock();
public void scheduleTask(Runnable task) {
lock.lock();
try { task.run(); } finally { lock.unlock(); }
}
}4. Resource Pool Management
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ResourceManager {
private int availableResources;
private Lock lock = new ReentrantLock();
public ResourceManager(int initialResources) { availableResources = initialResources; }
public Resource acquireResource() {
lock.lock();
try {
if (availableResources > 0) {
availableResources--;
return new Resource();
}
return null;
} finally { lock.unlock(); }
}
public void releaseResource() {
lock.lock();
try { availableResources++; } finally { lock.unlock(); }
}
private class Resource { /* implementation */ }
}5. Message Queue
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
public class MessageQueue {
private Queue<String> queue = new ConcurrentLinkedQueue<>();
public void sendMessage(String message) { queue.offer(message); }
public String receiveMessage() { return queue.poll(); }
}Lock Best Practices
1. Avoid Deadlocks
public class DeadlockExample {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public void method1() {
synchronized (lock1) {
synchronized (lock2) { /* ... */ }
}
}
public void method2() {
synchronized (lock2) {
synchronized (lock1) { /* ... */ }
}
}
}Ensure a consistent lock acquisition order or use timeout mechanisms to prevent circular wait conditions.
2. Control Lock Granularity
public class LockGranularityExample {
private final Object globalLock = new Object();
private int count = 0;
public void increment() {
synchronized (globalLock) { count++; }
}
public int getCount() {
synchronized (globalLock) { return count; }
}
}Fine‑grained locks (e.g., read‑write locks) can reduce contention compared to a single global lock.
3. Avoid Excessive Locks
public class TooManyLocksExample {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public void method1() { synchronized (lock1) { /* ... */ } }
public void method2() { synchronized (lock2) { /* ... */ } }
public void method3() { synchronized (lock1) { /* ... */ } }
}Reusing locks where possible reduces lock‑management overhead and improves throughput.
4. Resource Cleanup
public class ResourceCleanupExample {
private final Object lock = new Object();
private List<Resource> resources = new ArrayList<>();
public void addResource(Resource resource) {
synchronized (lock) { resources.add(resource); }
}
public void closeResources() {
synchronized (lock) {
for (Resource r : resources) { r.close(); }
resources.clear();
}
}
private class Resource { void close() { /* release */ } }
}5. Concurrency Testing
import java.util.concurrent.CountDownLatch;
public class ConcurrentTestExample {
private final Object lock = new Object();
private int count = 0;
public void increment() { synchronized (lock) { count++; } }
public int getCount() { synchronized (lock) { return count; } }
public static void main(String[] args) throws Exception {
final ConcurrentTestExample ex = new ConcurrentTestExample();
int threads = 10, incPerThread = 1000;
CountDownLatch latch = new CountDownLatch(threads);
for (int i = 0; i < threads; i++) {
new Thread(() -> {
for (int j = 0; j < incPerThread; j++) ex.increment();
latch.countDown();
}).start();
}
latch.await();
System.out.println("Final count: " + ex.getCount());
}
}Conclusion
Locks are indispensable tools for achieving thread safety in Java multithreaded programs. Understanding the characteristics, appropriate use cases, and best‑practice patterns for ReentrantLock, synchronized, ReadWriteLock, and spin locks enables developers to write correct, high‑performance, and scalable concurrent applications.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
