# Redis for Distributed Locking in Java Applications: SET NX PX and RedLock Implementation Guide

> Master Redis distributed locking in Java using SET NX PX and RedLock. Learn atomic Lua deletion and multi-master setups for robust high-availability locks. Explore advanced Java techniques.

- Repository: [Doocs/advanced-java](https://github.com/doocs/advanced-java)
- Tags: how-to-guide
- Published: 2026-02-28

---

**Use Redis `SET key value NX PX` with a random token and atomic Lua deletion for basic distributed locks, or implement the RedLock algorithm across multiple Redis masters for high-availability locking, as architected in the `doocs/advanced-java` repository.**

The `doocs/advanced-java` repository provides comprehensive architectural guidance on implementing **Redis for distributed locking in Java applications**, detailing both single-instance primitives and cluster-aware algorithms. This article translates those patterns into production-ready code, demonstrating how to safely coordinate distributed JVM processes using Redis commands and the RedLock protocol described in [`docs/distributed-system/distributed-lock-redis-vs-zookeeper.md`](https://github.com/doocs/advanced-java/blob/main/docs/distributed-system/distributed-lock-redis-vs-zookeeper.md).

## Basic Redis Lock with SET NX PX

The foundational locking mechanism relies on Redis's atomic `SET` command with conditional flags. According to the source documentation [25-30], this approach guarantees exclusive acquisition without race conditions.

### Acquiring Locks Atomically

Use the **`NX`** (only set if Not eXists) and **`PX`** (expiration in milliseconds) flags to establish ownership with automatic deadlock prevention. This single atomic operation ensures that if a client crashes, the lock expires automatically after the specified TTL.

### The Random Token Pattern

Every client must generate a **unique random value** (such as a `UUID`) to store as the lock's content. If a client holds the lock longer than the TTL, the key expires and another client may acquire it. When releasing, the original client must verify the stored value matches its token; otherwise, it would delete a lock currently owned by another process [40-45].

### Atomic Release via Lua Script

Since Redis lacks a native "delete if value equals" operation, implement safe lock release using a Lua script that executes atomically on the server:

```java
String lua =
    "if redis.call('get', KEYS[1]) == ARGV[1] then " +
    "   return redis.call('del', KEYS[1]) " +
    "else " +
    "   return 0 " +
    "end";
jedis.eval(lua, Collections.singletonList(LOCK_KEY),
        Collections.singletonList(LOCK_VALUE));

```

This script prevents the "check-then-delete" race condition by evaluating ownership and deletion in a single server-side operation.

## High-Availability with the RedLock Algorithm

For fault-tolerant locking across Redis cluster failures, the documentation describes the **RedLock algorithm** [54-60]. This protocol requires creating the same lock identifier on a majority of independent Redis master nodes (e.g., 3 of 5).

The client measures the total elapsed time for acquiring locks across all nodes. If this duration exceeds the lock's TTL, the attempt fails and the client rolls back by deleting keys from any nodes where acquisition succeeded. This ensures that only valid, non-expired locks are considered held.

## Redis vs Zookeeper: Architectural Trade-offs

The repository provides a detailed comparison between Redis-based locks and Zookeeper coordination [335-340]:

- **Redis locking** requires clients to poll repeatedly using `SET … NX` commands, consuming CPU cycles and network bandwidth until the lock becomes available.
- **Zookeeper locking** utilizes **ephemeral znodes** that automatically delete when the client session expires, eliminating polling overhead and TTL management concerns, though it requires maintaining persistent sessions to the Zookeeper ensemble.

## Complete Java Implementation Examples

### Single-Instance Lock with Jedis

The following implementation demonstrates the basic lock pattern using the Jedis client, corresponding to the architecture described in the documentation [25-30, 40-45]:

```java
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;
import java.util.Collections;
import java.util.UUID;

public class SimpleRedisLock {
    private static final String LOCK_KEY = "my_lock";
    private static final String LOCK_VALUE = UUID.randomUUID().toString();
    private static final int TTL_MS = 30_000;
    
    private final Jedis jedis = new Jedis("localhost", 6379);
    
    public boolean tryLock() {
        SetParams params = new SetParams()
                .nx()
                .px(TTL_MS);
        String result = jedis.set(LOCK_KEY, LOCK_VALUE, params);
        return "OK".equals(result);
    }
    
    public void unlock() {
        String lua =
                "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                "   return redis.call('del', KEYS[1]) " +
                "else " +
                "   return 0 " +
                "end";
        jedis.eval(lua, Collections.singletonList(LOCK_KEY),
                Collections.singletonList(LOCK_VALUE));
    }
}

```

### Cluster-Aware Locking with Redisson

For RedLock implementations across multiple masters, use the Redisson client which internally handles the algorithm described in the documentation [54-60]:

```java
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import java.util.concurrent.TimeUnit;

public class RedLockExample {
    public static void main(String[] args) throws InterruptedException {
        Config cfg = new Config();
        cfg.useClusterServers()
           .addNodeAddress("redis://127.0.0.1:7000",
                           "redis://127.0.0.1:7001",
                           "redis://127.0.0.1:7002");
        
        RedissonClient redisson = Redisson.create(cfg);
        RLock lock = redisson.getLock("order-id-12345");
        
        if (lock.tryLock(5, 10, TimeUnit.SECONDS)) {
            try {
                System.out.println("Lock acquired, processing order...");
            } finally {
                lock.unlock();
            }
        }
        redisson.shutdown();
    }
}

```

### Zookeeper Comparison Implementation

For architectural reference, here is the Zookeeper ephemeral znode approach detailed in the documentation [67-71]:

```java
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

public class ZkLock implements Watcher {
    private ZooKeeper zk;
    private final String lockPath = "/my_lock";
    
    public ZkLock() throws Exception {
        zk = new ZooKeeper("127.0.0.1:2181", 30000, this);
    }
    
    public boolean tryLock() throws KeeperException, InterruptedException {
        try {
            zk.create(lockPath, new byte[0],
                      ZooDefs.Ids.OPEN_ACL_UNSAFE,
                      CreateMode.EPHEMERAL);
            return true;
        } catch (KeeperException.NodeExistsException e) {
            zk.exists(lockPath, true);
            return false;
        }
    }
    
    public void unlock() throws KeeperException, InterruptedException {
        zk.delete(lockPath, -1);
    }
    
    @Override
    public void process(WatchedEvent event) {
        // Handle session events
    }
}

```

## Summary

- Use **`SET … NX PX`** with a random token for basic single-instance locks, ensuring automatic expiration prevents deadlocks.
- Implement **atomic deletion via Lua scripts** to verify ownership before releasing locks, preventing accidental removal of another client's lock.
- Deploy the **RedLock algorithm** across multiple Redis masters (majority quorum) for high-availability scenarios where individual node failures must not compromise locking safety.
- Consider **Zookeeper ephemeral znodes** as an alternative when you require session-based automatic cleanup rather than TTL-based expiration and polling.

## Frequently Asked Questions

### Why is a random value required for Redis distributed locks?

A random token (such as a UUID) identifies the specific client instance holding the lock. If a client's operation exceeds the TTL and the lock expires, another client may acquire the same key. Without the token verification in the Lua deletion script, the first client could accidentally delete the second client's lock upon completion, violating mutual exclusion.

### What is the RedLock algorithm and when should I use it?

The RedLock algorithm creates the same lock identifier on a majority of independent Redis master nodes (typically 3 of 5). It measures the total time spent acquiring these locks; if the duration exceeds the lock's TTL, the attempt fails and partial locks are rolled back. Use this when operating a Redis cluster where individual node failures must not create split-brain locking scenarios.

### How does Redis locking compare to Zookeeper for distributed coordination?

Redis locks require active polling with `SET … NX` commands, consuming CPU and network resources until acquisition succeeds [335-337]. Zookeeper utilizes ephemeral znodes that automatically delete when the client session expires, eliminating the need for TTL management and reducing polling overhead, though it requires maintaining persistent TCP sessions to Zookeeper ensemble nodes [335-340].

### Can I use SETNX and EXPIRE separately instead of SET NX PX?

No. Separate `SETNX` and `EXPIRE` commands create a race condition where the client could crash between the two commands, leaving a permanent lock without expiration. The atomic `SET … NX PX` operation ensures the key and its TTL are set in a single, indivisible step, which is the pattern recommended in the source documentation [25-30].