Understanding Redisson from Scratch: A Java Distributed Toolbox Guide and Hands‑On

This article introduces Redisson, a Redis‑based Java client that wraps Redis commands into familiar Java concurrency primitives, compares it with Jedis and Lettuce, explains why custom distributed locks are error‑prone, and provides step‑by‑step code for configuring, using, and integrating its core features such as locks, maps, queues, and rate limiters in Spring Boot.

The Dominant Programmer
The Dominant Programmer
The Dominant Programmer
Understanding Redisson from Scratch: A Java Distributed Toolbox Guide and Hands‑On

What is Redisson

Definition

Redisson is a Java client framework built on Redis that wraps low‑level Redis commands into Java‑familiar data structures and tools (locks, queues, maps, semaphores, etc.), allowing developers to use distributed features as if they were local objects.

Analogy

java.util.HashMap

RMap (distributed map, data stored in Redis, shared across service instances) java.util.concurrent.locks.ReentrantLockRLock (distributed lock, lock stored in Redis, works across JVMs) java.util.concurrent.BlockingQueueRBlockingQueue (distributed queue, multiple consumers) java.util.concurrent.SemaphoreRSemaphore (distributed semaphore, rate‑limit across instances)

Redisson vs Jedis vs Lettuce

Jedis – low‑level Redis client, sends commands directly, lightweight.

Lettuce – low‑level client based on Netty, supports async, default in Spring Boot.

Redisson – high‑level client that encapsulates distributed locks, collections, queues and other advanced features.

// Jedis: direct Redis commands
jedis.set("key", "value");
jedis.setnx("lock-key", "holder");

// Lettuce (via Spring RedisTemplate): also command‑based
redisTemplate.opsForValue().set("key", "value");
redisTemplate.opsForValue().setIfAbsent("lock-key", "holder");

// Redisson: object‑oriented, hides low‑level commands
RLock lock = redissonClient.getLock("lock-key");
lock.lock(); // internally handles SETNX, expiration, watchdog, etc.

Why use Redisson

Pain points of implementing a distributed lock yourself

public boolean tryLock(String key, String holderId, long timeout) {
    // 1. Acquire lock (SETNX + expiration, must be atomic)
    Boolean success = redisTemplate.opsForValue()
        .setIfAbsent(key, holderId, timeout, TimeUnit.SECONDS);
    return Boolean.TRUE.equals(success);
}

public void unlock(String key, String holderId) {
    // 2. Release lock (verify holder, atomic via Lua)
    String script = "if redis.call('get',KEYS[1]) == ARGV[1] then " +
                    "return redis.call('del',KEYS[1]) else return 0 end";
    redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
        Collections.singletonList(key), holderId);
    // Additional concerns: watchdog renewal, re‑entrancy, fairness, RedLock, async, listeners.
}

Redisson lock usage

RLock lock = redissonClient.getLock("my-lock");
lock.lock();
try {
    // business logic
} finally {
    lock.unlock();
}

Atomic lock acquisition via Lua script

Holder verification on release

Watchdog‑based automatic lease renewal

Re‑entrancy counting

Waiting queue and fairness

Compatibility with cluster and sentinel modes

Quick start

Maven dependency

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.27.0</version>
</dependency>

Connection configuration

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: your-password

or Redisson's own redisson.yml:

singleServerConfig:
  address: "redis://127.0.0.1:6379"
  password: "your-password"
  connectionMinimumIdleSize: 5
  connectionPoolSize: 10

Bean injection example

@Service
public class OrderServiceImpl implements OrderService {
    @Resource
    private RedissonClient redissonClient;

    public void processOrder(Integer orderId) {
        RLock lock = redissonClient.getLock("order-" + orderId);
        boolean acquired = lock.tryLock(10, 30, TimeUnit.SECONDS);
        if (!acquired) {
            throw new RuntimeException("Failed to acquire lock");
        }
        try {
            doProcess(orderId);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}

Core Redisson features

Distributed lock (RLock)

RLock lock = redissonClient.getLock("my-lock");
// blocking lock
lock.lock();
// tryLock with wait time (10 s)
bool ok = lock.tryLock(10, TimeUnit.SECONDS);
// tryLock with wait time and lease time (30 s)
ok = lock.tryLock(10, 30, TimeUnit.SECONDS);
// tryLock with watchdog (no lease time)
ok = lock.tryLock(10, TimeUnit.SECONDS);
lock.unlock();

Fair lock (RFairLock)

RLock fairLock = redissonClient.getFairLock("fair-lock");
fairLock.lock();
try {
    // business logic
} finally {
    fairLock.unlock();
}

Read‑write lock (RReadWriteLock)

RReadWriteLock rwLock = redissonClient.getReadWriteLock("rw-lock");
RLock readLock = rwLock.readLock();
readLock.lock();
try { /* read */ } finally { readLock.unlock(); }

RLock writeLock = rwLock.writeLock();
writeLock.lock();
try { /* write */ } finally { writeLock.unlock(); }

Distributed map (RMap)

RMap<String, UserInfo> userCache = redissonClient.getMap("user-cache");
userCache.put("user-123", userInfo);
UserInfo info = userCache.get("user-123");
userCache.put("user-123", userInfo, 30, TimeUnit.MINUTES);

Distributed queue (RBlockingQueue)

// producer
RBlockingQueue<String> queue = redissonClient.getBlockingQueue("task-queue");
queue.offer("task-001");

// consumer (blocks if empty)
String task = queue.take();
processTask(task);

Rate limiter (RRateLimiter)

RRateLimiter limiter = redissonClient.getRateLimiter("api-limiter");
limiter.trySetRate(RateType.OVERALL, 10, 1, RateIntervalUnit.SECONDS);
if (limiter.tryAcquire()) {
    processRequest();
} else {
    throw new RuntimeException("Too many requests");
}

RLock internal mechanics

Lock acquisition script (simplified)

-- if lock does not exist
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 lock exists and is owned by the same holder (re‑enter)
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
-- lock held by another client, return remaining TTL
return redis.call('pttl', KEYS[1])

Data structure in Redis

Key: "my-lock"
Type: Hash
Value: "holder-id-thread-1" → 2   (re‑enter count = 2)
TTL: 30000 ms

Unlock script (simplified)

-- verify ownership
if redis.call('hexists', KEYS[1], ARGV[1]) == 0 then
    return nil
end
-- decrement re‑enter count
local counter = redis.call('hincrby', KEYS[1], ARGV[1], -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[3])
    return 1
end

Spring Boot integration

Auto‑configuration (simplest)

@Service
public class MyService {
    @Resource
    private RedissonClient redissonClient;

    public void doSomething() {
        RLock lock = redissonClient.getLock("key");
        // …
    }
}

Manual configuration

@Configuration
public class RedissonConfig {
    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer()
            .setAddress("redis://127.0.0.1:6379")
            .setPassword("password")
            .setConnectionMinimumIdleSize(5)
            .setConnectionPoolSize(20)
            .setTimeout(3000)
            .setRetryAttempts(3);
        return Redisson.create(config);
    }
}

Cluster mode

@Bean
public RedissonClient redissonClient() {
    Config config = new Config();
    config.useClusterServers()
        .addNodeAddress("redis://node1:6379", "redis://node2:6379", "redis://node3:6379")
        .setPassword("password");
    return Redisson.create(config);
}

Real‑world example: Stock deduction

Full implementation

@Service
public class StockServiceImpl implements StockService {
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private StockRepository stockRepository;

    @Transactional(rollbackFor = Exception.class)
    public boolean deductStock(Integer skuId, Integer quantity) {
        String lockKey = "stock-deduct-" + skuId;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            boolean acquired = lock.tryLock(5, TimeUnit.SECONDS);
            if (!acquired) {
                log.warn("Lock timeout for skuId:{}", skuId);
                return false;
            }
            Stock stock = stockRepository.findBySkuId(skuId);
            if (stock == null || stock.getQuantity() < quantity) {
                log.info("Insufficient stock, skuId:{}, current:{}, needed:{}",
                         skuId, stock == null ? 0 : stock.getQuantity(), quantity);
                return false;
            }
            stock.setQuantity(stock.getQuantity() - quantity);
            stockRepository.save(stock);
            log.info("Stock deducted, skuId:{}, deducted:{}, remaining:{}",
                     skuId, quantity, stock.getQuantity());
            return true;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.warn("Lock interrupted for skuId:{}", skuId);
            return false;
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}

Key steps

// 1. Obtain lock object (not yet locked)
RLock lock = redissonClient.getLock(lockKey);

// 2. tryLock(waitTime) – attempts to acquire, waits up to 5 s
boolean acquired = lock.tryLock(5, TimeUnit.SECONDS);

// 3. isHeldByCurrentThread() – ensures only the owner releases
if (lock.isHeldByCurrentThread()) {
    lock.unlock();
}

Performance notes

Lock acquisition and release typically 1–3 ms (network dependent).

Redisson adds modest overhead compared with raw RedisTemplate but remains much faster than ZooKeeper‑based locks.

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.

JavaRedisSpring BootDistributed LockRedissonRate Limiter
The Dominant Programmer
Written by

The Dominant Programmer

Resources and tutorials for programmers' advanced learning journey. Advanced tracks in Java, Python, and C#. Blog: https://blog.csdn.net/badao_liumang_qizhi

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.