Mastering Java Locks: From ReentrantLock to Distributed Redisson & Zookeeper
This article explains the fundamentals and practical usage of various Java locking mechanisms—including ReentrantLock, synchronized, ReentrantReadWriteLock, and the Atomic and Concurrent families—then explores distributed lock solutions such as Redisson’s multiple lock types and Zookeeper/Curator, comparing their performance, consistency, and suitability for different scenarios.
ReentrantLock
ReentrantLock is a mutual‑exclusive reentrant lock. Mutual exclusion means only one thread can hold the lock at a time, while reentrancy allows the same thread to acquire the lock multiple times without blocking.
Common Methods
Typical Scenario
In recursive business logic (e.g., a tree‑structured process) the same lock must be held across multiple nested method calls. A reentrant lock prevents dead‑lock in such recursion.
Synchronized
Synchronized implements a pessimistic lock. By default it starts as a biased lock, upgrades to lightweight, and finally to heavyweight under contention. It provides reentrancy, exclusivity, and both fair and non‑fair behavior.
Usage Example
public synchronized void someMethod() {
// method body
}
public void someMethod() {
synchronized (lockObject) {
// critical section
}
}
public static synchronized void someStaticMethod() {
// static method body
}
public class MyClass {
public void method1() {
synchronized(MyClass.class) {
// synchronized block
}
}
public synchronized void method2() {
// synchronized method
}
}ReentrantReadWriteLock
Java’s ReentrantReadWriteLock allows multiple concurrent readers but only one writer. It implements read‑write and shared lock algorithms.
Common Methods
Atomic Family
The Atomic classes (e.g., AtomicInteger) implement CAS algorithms, solve the ABA problem, and provide lock‑free atomic operations. They also include a CLH spin‑lock implementation.
Common Methods
Concurrent Family
Concurrent utilities such as ConcurrentHashMap use segmented locking: the map is divided into multiple segments, each with its own lock, reducing contention and improving throughput.
Typical Use Case
In high‑traffic scenarios like flash‑sale inventory deduction, the inventory can be partitioned into many segments (e.g., 100) so that concurrent threads lock only their own segment, dramatically lowering lock conflicts.
Distributed Lock Scenarios: Redisson and Zookeeper
Redisson Overview
Redisson provides eight distributed lock implementations on top of Redis, all implementing java.util.concurrent.locks.Lock. It also offers asynchronous, reactive, and RxJava2 APIs.
1. Reentrant Lock (RLock)
public static void main(String[] args) throws InterruptedException {
RedissonClient redisson = Redisson.create();
RLock lock = redisson.getLock("lock");
lock.lock(2, TimeUnit.SECONDS);
Thread t = new Thread() {
public void run() {
RLock lock1 = redisson.getLock("lock");
lock1.lock();
lock1.unlock();
};
};
t.start();
t.join();
redisson.shutdown();
}Redisson uses a watchdog mechanism to automatically extend the lock lease while the client is alive, defaulting to a 30‑second timeout.
2. Fair Lock
public static void main(String[] args) throws InterruptedException {
RedissonClient redisson = Redisson.create();
RLock lock = redisson.getFairLock("test");
int size = 10;
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < size; i++) {
Thread t = new Thread() {
public void run() {
lock.lock();
lock.unlock();
};
};
threads.add(t);
}
for (Thread thread : threads) {
thread.start();
thread.join(5);
}
for (Thread thread : threads) {
thread.join();
}
}3. MultiLock
public static void main(String[] args) throws InterruptedException {
RedissonClient client = Redisson.create();
RLock lock1 = client.getLock("lock1");
RLock lock2 = client.getLock("lock2");
RLock lock3 = client.getLock("lock3");
RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
lock.lock();
// do work
lock.unlock();
}4. RedLock
RedissonClient client1 = Redisson.create();
RedissonClient client2 = Redisson.create();
RLock lock1 = client1.getLock("lock1");
RLock lock2 = client1.getLock("lock2");
RLock lock3 = client2.getLock("lock3");
RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
lock.lock();
// work
lock.unlock();
client1.shutdown();
client2.shutdown();5. ReadWriteLock (RReadWriteLock)
RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");
rwlock.readLock().lock();
// read work
rwlock.readLock().unlock();
rwlock.writeLock().lock();
// write work
rwlock.writeLock().unlock();6. Semaphore
RSemaphore s = redisson.getSemaphore("test");
s.trySetPermits(5);
s.acquire(3);
// ...
s.release();7. PermitExpirableSemaphore
RPermitExpirableSemaphore s = redisson.getPermitExpirableSemaphore("test");
String permitId = s.tryAcquire(100, 2, TimeUnit.SECONDS);
// use permitId
s.tryRelease(permitId);8. CountDownLatch
RCountDownLatch latch = redisson.getCountDownLatch("latch1");
latch.trySetCount(1);
// in another thread
latch.countDown();
// await
latch.await(550, TimeUnit.MILLISECONDS);Zookeeper Locks (Curator)
Zookeeper provides strong consistency (CP) but lower performance. Curator is a high‑level client that simplifies Zookeeper usage.
1. Reentrant Lock (InterProcessMutex)
public class ExampleClientThatLocks {
private final InterProcessMutex lock;
private final FakeLimitedResource resource;
private final String clientName;
public ExampleClientThatLocks(CuratorFramework client, String lockPath, FakeLimitedResource resource, String clientName) {
this.resource = resource;
this.clientName = clientName;
lock = new InterProcessMutex(client, lockPath);
}
public void doWork(long time, TimeUnit unit) throws Exception {
if (!lock.acquire(time, unit)) {
throw new IllegalStateException(clientName + " could not acquire the lock");
}
try {
System.out.println(clientName + " has the lock");
resource.use();
} finally {
System.out.println(clientName + " releasing the lock");
lock.release();
}
}
}2. Non‑Reentrant Lock (InterProcessSemaphoreMutex)
Works like a standard mutex but cannot be re‑entered by the same thread.
3. Reentrant ReadWriteLock (InterProcessReadWriteLock)
Provides separate read and write locks; multiple readers can coexist while a writer has exclusive access.
4. Semaphore (InterProcessSemaphoreV2)
Implements a counting semaphore similar to java.util.concurrent.Semaphore, with lease‑based permits.
5. Multi‑Lock (InterProcessMultiLock)
Groups several locks into a single composite lock; all must be acquired successfully, otherwise all are released.
CAP Considerations
In distributed environments the CAP theorem applies: you can only achieve two of Consistency, Availability, and Partition tolerance at the same time. Choose CP‑type locks (e.g., Zookeeper) when strong consistency is required, and AP‑type locks (e.g., Redisson) when high availability is more important.
Conclusion
For most business scenarios Redisson’s distributed locks are sufficient, and eventual consistency can handle occasional data divergence. However, if your system cannot tolerate inconsistency, prefer Zookeeper‑based CP locks despite their lower performance.
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.
JD Cloud Developers
JD Cloud Developers (Developer of JD Technology) is a JD Technology Group platform offering technical sharing and communication for AI, cloud computing, IoT and related developers. It publishes JD product technical information, industry content, and tech event news. Embrace technology and partner with developers to envision the future.
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.
