Create a Custom Lock in Java
This deep dive explains the problem model, concurrency contract, Java implementation, and real-world caveats you should know before using this pattern in production.
Lock implementation
public class Lock {
private boolean isLocked = false;
public synchronized void lock() throws InterruptedException {
while (isLocked) {
wait();
}
isLocked = true;
}
public synchronized void unlock() {
isLocked = false;
notify();
}
}
Usage
lock.lock();
try {
// critical section
} finally {
lock.unlock();
}
Improvement for production
The simple version does not track ownership. A safer version checks owner thread:
private Thread owner;
public synchronized void lock() throws InterruptedException {
while (isLocked) {
wait();
}
isLocked = true;
owner = Thread.currentThread();
}
public synchronized void unlock() {
if (Thread.currentThread() != owner) {
throw new IllegalMonitorStateException("Current thread does not own lock");
}
isLocked = false;
owner = null;
notify();
}
In real code, prefer ReentrantLock unless this is for learning.
Production API Equivalent (ReentrantLock)
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class AccountService {
private final Lock lock = new ReentrantLock(true); // fair lock
private int balance = 0;
public void deposit(int amount) {
lock.lock();
try {
balance += amount;
} finally {
lock.unlock();
}
}
}
If you need separate read/write access, use ReadWriteLock. For optimistic reads on highly contended state, evaluate StampedLock.
Key Takeaways
- Correctness comes before throughput in concurrent code.
- Prefer proven JDK concurrency utilities in production over custom implementations.
- Always account for interruption, waiting conditions, and race windows.