Redisson Distributed Lock: Maven Setup, Simple Usage, Redis Commands, Lua Script Semantics, and Source Code Analysis

This article presents a concise Redisson distributed‑lock example with Maven configuration, demonstrates basic lock usage, enumerates the Redis commands and Lua script semantics involved, and provides a detailed walkthrough of the relevant Java source code, highlighting key implementation details.

Top Architect
Top Architect
Top Architect
Redisson Distributed Lock: Maven Setup, Simple Usage, Redis Commands, Lua Script Semantics, and Source Code Analysis

This article provides a simple configuration example and source‑code analysis of Redisson's distributed lock, covering Maven dependencies, basic usage, the Redis commands employed, Lua script semantics, and an in‑depth examination of the lock implementation.

Maven Configuration

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>2.2.12</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.6.0</version>
</dependency>

RedissonLock Simple Example

Redisson supports four connection modes—single, master/slave, Sentinel, and Cluster. The project uses the Sentinel mode.

Sentinel Configuration

Config config = new Config();
config.useSentinelServers()
      .addSentinelAddress("127.0.0.1:6479", "127.0.0.1:6489")
      .setMasterName("master")
      .setPassword("password")
      .setDatabase(0);
RedissonClient redisson = Redisson.create(config);

Simple Usage

RLock lock = redisson.getLock("test_lock");
try {
    boolean isLock = lock.tryLock();
    if (isLock) {
        doBusiness();
    }
} catch (exception e) {
    // handle exception
} finally {
    lock.unlock();
}

Redis Commands Used by the Lock

EXISTS key – returns 1 if the key exists, otherwise 0.

GETSET key value – sets a new value and returns the old one.

GET key – returns the string value of the key.

DEL key … – deletes one or more keys.

HSET key field value – sets a hash field.

HEXISTS key field – checks if a hash field exists.

HINCRBY key field increment – increments a hash field.

PEXPIRE key ms – sets a key’s TTL in milliseconds.

PUBLISH channel message – publishes a message to a channel.

Lua Script Semantics in Redisson

The Lua scripts executed by Redisson use redis.call() to run Redis commands, KEYS[1] to refer to the lock name, and ARGV[n] for script arguments. nil and false are treated equivalently as “no value”. The script runs atomically, providing the lock’s core functionality.

Source Code Analysis

RLock Interface

public interface RLock extends Lock, RExpirable {
    void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException;
    boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException;
    void lock(long leaseTime, TimeUnit unit);
    void forceUnlock();
    boolean isLocked();
    boolean isHeldByCurrentThread();
    int getHoldCount();
    Future<Void> unlockAsync();
    Future<Boolean> tryLockAsync();
    Future<Void> lockAsync();
    Future<Void> lockAsync(long leaseTime, TimeUnit unit);
    Future<Boolean> tryLockAsync(long waitTime, TimeUnit unit);
    Future<Boolean> tryLockAsync(long waitTime, long leaseTime, TimeUnit unit);
}

The interface extends Lock and adds methods for lease time, re‑entrancy counters, and asynchronous operations. The leaseTime defaults to 30 seconds, after which Redis automatically releases the lock.

RedissonLock tryLock Implementation

Future<Long> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId) {
    internalLockLeaseTime = unit.toMillis(leaseTime);
    return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_LONG,
        "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]);",
        Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
}

The script first creates the lock if it does not exist, then increments the re‑entrancy counter when the same thread re‑acquires it, and finally returns the remaining TTL if the lock is held by another thread.

RedissonLock unlock Implementation

public void unlock() {
    Boolean opStatus = commandExecutor.evalWrite(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
        "if (redis.call('exists', KEYS[1]) == 0) then " +
        "redis.call('publish', KEYS[2], ARGV[1]); " +
        "return 1; " +
        "end;" +
        "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; " +
        "return nil;",
        Arrays.<Object>asList(getName(), getChannelName()), LockPubSub.unlockMessage, internalLockLeaseTime, getLockName(Thread.currentThread().getId()));
    if (opStatus == null) {
        throw new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: " + id + " thread-id: " + Thread.currentThread().getId());
    }
    if (opStatus) {
        cancelExpirationRenewal();
    }
}

If the lock does not exist, a unlock message is published. If the current thread does not own the lock, the script returns null. Otherwise the re‑entrancy counter is decremented; when it reaches zero the lock key is deleted and a release message is published.

Force Unlock

public void forceUnlock() {
    get(forceUnlockAsync());
}
Future<Boolean> forceUnlockAsync() {
    cancelExpirationRenewal();
    return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
        "if (redis.call('del', KEYS[1]) == 1) then " +
        "redis.call('publish', KEYS[2], ARGV[1]); " +
        "return 1 " +
        "else " +
        "return 0 " +
        "end",
        Arrays.<Object>asList(getName(), getChannelName()), LockPubSub.unlockMessage);
}

The force‑unlock method unconditionally deletes the lock key and publishes a release message, returning 1 on success.

Conclusion

This piece demonstrates a basic Redisson distributed‑lock test case, explains the underlying Redis commands and Lua scripts, and walks through the relevant Java source. Further analysis—such as the Netty code that Redisson uses for asynchronous handling—would be required for a complete understanding.

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.

JavaBackend Developmentredismavendistributed-lockLuaredisson
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

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.