How Redis Implements Distributed Locks and Automatic Renewal with Redisson

Redis can be used to create a distributed lock by storing a unique key-value pair with an expiration time, and Redisson enhances this with a watchdog mechanism that automatically renews the lock, handling lock acquisition, expiration, and release through Lua scripts and subscription-based waiting.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
How Redis Implements Distributed Locks and Automatic Renewal with Redisson

Redis Distributed Lock Implementation

Use a specific key as lock marker, store in Redis with a unique client identifier as value.

The key can be set only when it does not exist, ensuring mutual exclusion.

Set an expiration time to avoid deadlock if the process crashes.

After business processing, delete the key to release the lock, verifying the value so only the lock owner can release it.

Problem

If the lock expires after 30 seconds but the business logic runs longer (e.g., 40 seconds), another client may acquire the lock while the original task is still running.

Choosing an appropriate expiration time is difficult: too short increases the chance of premature expiration; too long prolongs lock holding when a client crashes.

Automatic Renewal

One approach is to set an initial lock time and start a daemon thread that periodically extends the lock's expiration.

Implementation is non‑trivial because the daemon must verify that the lock holder has not changed before renewing.

The daemon must avoid unnecessary renewals to conserve resources.

If the business task finishes, the daemon should stop.

Watchdog

Redisson provides a watchdog mechanism that automatically renews the lock.

Redisson tryLock

public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
    long time = unit.toMillis(waitTime);
    long current = System.currentTimeMillis();
    long threadId = Thread.currentThread().getId();
    // 1. try to acquire lock
    Long ttl = tryAcquire(leaseTime, unit, threadId);
    if (ttl == null) {
        return true;
    }
    // subtract elapsed time
    time -= System.currentTimeMillis() - current;
    if (time <= 0) {
        acquireFailed(threadId);
        return false;
    }
    current = System.currentTimeMillis();
    // subscribe to lock release events and wait
    RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);
    if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {
        if (!subscribeFuture.cancel(false)) {
            subscribeFuture.onComplete((res, e) -> {
                if (e == null) {
                    unsubscribe(subscribeFuture, threadId);
                }
            });
        }
        acquireFailed(threadId);
        return false;
    }
    try {
        time -= System.currentTimeMillis() - current;
        if (time <= 0) {
            acquireFailed(threadId);
            return false;
        }
        while (true) {
            long currentTime = System.currentTimeMillis();
            ttl = tryAcquire(leaseTime, unit, threadId);
            if (ttl == null) {
                return true;
            }
            time -= System.currentTimeMillis() - currentTime;
            if (time <= 0) {
                acquireFailed(threadId);
                return false;
            }
            // wait for lock release signal
            currentTime = System.currentTimeMillis();
            if (ttl >= 0 && ttl < time) {
                getEntry(threadId).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
            } else {
                getEntry(threadId).getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);
            }
            time -= System.currentTimeMillis() - currentTime;
            if (time <= 0) {
                acquireFailed(threadId);
                return false;
            }
        }
    } finally {
        // always unsubscribe
        unsubscribe(subscribeFuture, threadId);
    }
    return get(tryLockAsync(waitTime, leaseTime, unit));
}

tryLock returns null when lock is acquired; otherwise returns remaining TTL.

If acquisition fails, the client subscribes to lock release events and retries within the wait time.

The loop uses Redis pub/sub and a semaphore to block efficiently instead of busy‑waiting.

How the Watchdog Renews Automatically

When a client successfully acquires a lock, Redisson starts a watchdog thread. Every internalLockLeaseTime / 3 seconds (default 10 s for a 30 s lease), it checks whether the client still holds the lock; if so, it extends the key’s expiration using a Lua script.

private void scheduleExpirationRenewal(long threadId) {
    ExpirationEntry entry = new ExpirationEntry();
    ExpirationEntry oldEntry = EXPIRATION_RENEWAL_MAP.putIfAbsent(getEntryName(), entry);
    if (oldEntry != null) {
        oldEntry.addThreadId(threadId);
    } else {
        entry.addThreadId(threadId);
        renewExpiration();
    }
}
protected RFuture<Boolean> renewExpirationAsync(long threadId) {
    return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
        "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
        "redis.call('pexpire', KEYS[1], ARGV[1]); " +
        "return 1; " +
        "end; " +
        "return 0;",
        Collections.singletonList(getName()),
        internalLockLeaseTime, getLockName(threadId));
}

The watchdog stops when the client releases the lock or crashes; in the latter case the lock naturally expires after the lease time, allowing other clients to acquire it.

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.

Javaconcurrencyredisdistributed-lockredissonWatchdog
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.