Mastering Redis Distributed Locks with Jedis: Build a 100k-User Flash Sale Simulation

This article demonstrates how to implement Redis distributed locks using Jedis in Java, covering lock creation with SETNX, lock release via Lua scripts, and a high‑concurrency flash‑sale simulation with 100,000 virtual users, illustrating key concepts such as lock expiration, atomic operations, and performance testing.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
Mastering Redis Distributed Locks with Jedis: Build a 100k-User Flash Sale Simulation

This guide explains how to use Redis as a distributed lock mechanism in Java applications with the Jedis client, and shows a practical high‑concurrency flash‑sale simulation.

1. Jedis NX lock creation

How to delete a lock

Simulate a flash‑sale scenario with 100k concurrent users

Generate a lock using Jedis's SETNX

First, add the Jedis dependency to your Maven pom.xml:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

Lock creation strategy: A normal Redis key can be overwritten, so using SETNX ensures that only the first client can set the key successfully, providing mutual exclusion.

Lock expiration: Setting an expiration prevents a dead lock when the holder crashes; the lock will be released automatically after the timeout.

Implementation of the setnx method:

public boolean setnx(String key, String val) {
    Jedis jedis = null;
    try {
        jedis = jedisPool.getResource();
        if (jedis == null) {
            return false;
        }
        return jedis.set(key, val, "NX", "PX", 1000 * 60)
                     .equalsIgnoreCase("OK");
    } catch (Exception ex) {
    } finally {
        if (jedis != null) {
            jedis.close();
        }
    }
    return false;
}

The parameters of jedis.set are:

NX – set only if the key does not exist

PX – expiration time in milliseconds (EX for seconds)

Expose the method via a Spring MVC endpoint:

@GetMapping("/setnx/{key}/{val}")
public boolean setnx(@PathVariable String key, @PathVariable String val) {
    return jedisCom.setnx(key, val);
}

Calling the URL the first time returns true; subsequent calls return false because the key already exists.

2. How to delete the lock

After the business logic finishes, the lock should be released explicitly to avoid unnecessary waiting. The following method uses a Lua script to delete the key only if the stored value matches the caller's value:

public int delnx(String key, String val) {
    Jedis jedis = null;
    try {
        jedis = jedisPool.getResource();
        if (jedis == null) {
            return 0;
        }
        StringBuilder sbScript = new StringBuilder();
        sbScript.append("if redis.call('get','").append(key).append("')=='").append(val).append("' then ")
                .append("return redis.call('del','").append(key).append("') ")
                .append("else return 0 end");
        return Integer.valueOf(jedis.eval(sbScript.toString()).toString());
    } catch (Exception ex) {
    } finally {
        if (jedis != null) {
            jedis.close();
        }
    }
    return 0;
}

Expose the delete operation:

@GetMapping("/delnx/{key}/{val}")
public int delnx(@PathVariable String key, @PathVariable String val) {
    return jedisCom.delnx(key, val);
}

The val must be the same value used when the lock was created; otherwise the delete is ignored.

3. Simulating a flash‑sale with 100k users

Initialize inventory and create 100,000 virtual users, then use a parallel stream to simulate concurrent purchase attempts:

// total inventory
private long nKuCuen = 0;
// product key
private String shangpingKey = "computer_key";
// lock timeout (ms)
private int timeout = 30 * 1000;

@GetMapping("/qiangdan")
public List<String> qiangdan() {
    List<String> shopUsers = new ArrayList<>();
    List<String> users = new ArrayList<>();
    IntStream.range(0, 100000).parallel().forEach(b -> users.add("神牛-" + b));
    nKuCuen = 10; // only 10 items
    users.parallelStream().forEach(b -> {
        String shopUser = qiang(b);
        if (!StringUtils.isEmpty(shopUser)) {
            shopUsers.add(shopUser);
        }
    });
    return shopUsers;
}

private String qiang(String b) {
    long startTime = System.currentTimeMillis();
    while ((startTime + timeout) >= System.currentTimeMillis()) {
        if (nKuCuen <= 0) {
            break;
        }
        if (jedisCom.setnx(shangpingKey, b)) {
            try {
                if (nKuCuen <= 0) {
                    break;
                }
                // simulate order processing time
                TimeUnit.SECONDS.sleep(1);
                nKuCuen -= 1;
                return b + "抢单成功,所剩库存:" + nKuCuen;
            } finally {
                jedisCom.delnx(shangpingKey, b);
            }
        }
    }
    return "";
}

Key points of the simulation:

Use parallelStream() to model many concurrent users.

Retry acquiring the lock within a timeout window.

Check inventory before and after obtaining the lock.

Acquire the lock with jedisCom.setnx, process the order, then release it with jedisCom.delnx.

The final output lists the users who successfully purchased the product.

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.

JavaconcurrencyredisJedisdistributed-lockSpringBoot
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.