How Redisson Implements Distributed Locks: From Basic Concepts to Fair Lock Mechanics

This article explains Redisson's in‑memory data grid features, compares it with Jedis and Lettuce, and walks through the implementation of re‑entrant, watchdog‑enabled, and fair distributed locks using Redis Lua scripts and Java code examples.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
How Redisson Implements Distributed Locks: From Basic Concepts to Fair Lock Mechanics

Redisson Overview

Redisson is a Java in‑memory data grid built on Redis that provides distributed objects (BitSet, Set, Map, List, Queue, etc.) and services (Lock, Semaphore, Bloom filter, Remote service, Scheduler, etc.) to simplify Redis usage.

Compared with Jedis (a direct Redis command wrapper) and Lettuce (a Netty‑based client supporting clustering), Redisson adds higher‑level abstractions such as distributed locks.

Distributed Lock Basics

How to implement a simple Redis distributed lock

Using Spring Data Redis, a lock can be acquired with setIfAbsent (SETNX + EXPIRE) and released by checking the lock value before deletion.

public Boolean tryLock(String key, String value, long timeout, TimeUnit unit) {
    return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);
}

public void unlock(String lockName, String uuid) {
    if (uuid.equals(redisTemplate.opsForValue().get(lockName))) {
        redisTemplate.opsForValue().del(lockName);
    }
}

This 1.0 version is not atomic for get and delete, so a Lua script is introduced.

Lua script for unlocking

if redis.call('get', KEYS[1]) == ARGV[1] then
    return redis.call('del', KEYS[1])
else
    return 0
end

The script is executed via DefaultRedisScript in Spring.

Re‑entrant Lock Implementation

To support re‑entrancy, a Redis hash stores threadId → count. The lock acquisition Lua checks whether the hash exists; if not, it creates it with count 1 and sets an expiration. If the thread already holds the lock, the count is incremented.

if (redis.call('exists', KEYS[1]) == 0) then
    redis.call('hset', KEYS[1], ARGV[2], 1);
    redis.call('pexpire', KEYS[1], ARGV[1]);
    return nil;
end;
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
    redis.call('hincrby', KEYS[1], ARGV[2], 1);
    redis.call('pexpire', KEYS[1], ARGV[1]);
    return nil;
end;
return redis.call('pttl', KEYS[1]);

Unlocking decrements the counter; when it reaches zero the hash is deleted.

if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then
    return nil;
end;
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);
if (counter > 0) then
    redis.call('pexpire', KEYS[1], ARGV[2]);
    return 0;
else
    redis.call('del', KEYS[1]);
    redis.call('publish', KEYS[2], ARGV[1]);
    return 1;
end

Watchdog (Lock Renewal)

If no lease time is set, Redisson starts a watchdog that periodically (≈ leaseTime/3) renews the lock expiration using another Lua script, ensuring the lock stays alive while the owning thread is active.

if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
    redis.call('pexpire', KEYS[1], ARGV[1]);
    return 1;
end;
return 0;

Fair Lock

RedissonFairLock guarantees FIFO acquisition using a Redis list (waiting queue) and a sorted set (timeouts). The Lua script performs six steps:

Clean expired entries from the queue.

If the lock does not exist and the queue is empty or the head equals the current thread, acquire the lock.

Handle re‑entrancy.

Return the remaining TTL for the current thread.

Compute the tail node's TTL.

Append the current thread to the queue with a score of ttl + waitTime + now.

while true do
    local first = redis.call('lindex', KEYS[2], 0);
    if first == false then break; end;
    local timeout = tonumber(redis.call('zscore', KEYS[3], first));
    if timeout <= tonumber(ARGV[4]) then
        redis.call('zrem', KEYS[3], first);
        redis.call('lpop', KEYS[2]);
    else
        break;
    end;
end;

if (redis.call('exists', KEYS[1]) == 0) and ((redis.call('exists', KEYS[2]) == 0) or (redis.call('lindex', KEYS[2], 0) == ARGV[2])) then
    redis.call('lpop', KEYS[2]);
    redis.call('zrem', KEYS[3], ARGV[2]);
    redis.call('hset', KEYS[1], ARGV[2], 1);
    redis.call('pexpire', KEYS[1], ARGV[1]);
    return nil;
end;

if redis.call('hexists', KEYS[1], ARGV[2]) == 1 then
    redis.call('hincrby', KEYS[1], ARGV[2], 1);
    redis.call('pexpire', KEYS[1], ARGV[1]);
    return nil;
end;

local ttl = redis.call('zscore', KEYS[3], ARGV[2]);
if ttl ~= false then
    return ttl - tonumber(ARGV[3]) - tonumber(ARGV[4]);
end;

local last = redis.call('lindex', KEYS[2], -1);
local ttl;
if last ~= false and last ~= ARGV[2] then
    ttl = tonumber(redis.call('zscore', KEYS[3], last)) - tonumber(ARGV[4]);
else
    ttl = redis.call('pttl', KEYS[1]);
end;

local timeout = ttl + tonumber(ARGV[3]) + tonumber(ARGV[4]);
if redis.call('zadd', KEYS[3], timeout, ARGV[2]) == 1 then
    redis.call('rpush', KEYS[2], ARGV[2]);
end;
return ttl;

This mechanism ensures that threads acquire the lock in the order they arrived, similar to Java's AQS fair lock.

Conclusion

Redisson provides a rich set of distributed synchronization primitives built on Redis, including re‑entrant locks, watchdog‑based lease renewal, and FIFO fair locks. Understanding the underlying Lua scripts and Redis data structures (hashes, lists, sorted sets) helps developers appreciate how Redisson achieves correctness and performance in single‑node and multi‑node environments.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javaredisdistributed-lockLuaredissonfair lock
Java High-Performance Architecture
Written by

Java High-Performance Architecture

Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.