Why Sharing a Jedis Instance Across Threads Breaks Redis – Understanding Connection Pools

This article explains the concept of connection pools, compares pooled and direct TCP connections, demonstrates thread‑safety issues when reusing a Jedis instance in multithreaded code, and shows how to fix the problem by using JedisPool with proper resource management.

JavaEdge
JavaEdge
JavaEdge
Why Sharing a Jedis Instance Across Threads Breaks Redis – Understanding Connection Pools

What Is a Connection Pool?

A connection pool provides two external interfaces—acquire a connection and return a connection—and exposes configurable parameters such as minimum idle connections and maximum connections. Internally it handles connection establishment, heartbeat maintenance, connection management, idle‑connection reclamation, and connection‑availability detection.

Connection‑Pool vs. Direct Connection

The typical XXXPool class implements the pool logic: first obtain an XXXConnection, use it to request the server, then return the connection. XXXPool must be thread‑safe because it can be accessed concurrently, while the individual XXXConnection objects are not thread‑safe.

Built‑In Connection Pools in Client SDKs

Many middleware and database client SDKs embed a connection pool. The SDK maintains the pool internally, so users interact with a higher‑level client class (e.g., XXXClient) without worrying about acquiring or releasing connections. The client class itself is thread‑safe.

Non‑Pooled Usage Pitfalls

When a client SDK does not use a pool, each operation creates a new TCP connection (a short‑lived connection). This approach incurs connection‑setup overhead, lacks thread safety, and generally yields poorer performance. The API is usually named XXXConnection to distinguish it from the pooled variant.

Case Study: Misusing Jedis in a Multithreaded Environment

First, two keys are initialized in Redis:

@PostConstruct
public void init() {
    try (Jedis jedis = new Jedis("127.0.0.1", 6379)) {
        Assert.isTrue("OK".equals(jedis.set("a", "1")), "set a = 1 return OK");
        Assert.isTrue("OK".equals(jedis.set("b", "2")), "set b = 2 return OK");
    }
}

Then two threads share a single Jedis instance, each repeatedly reading a different key:

Jedis jedis = new Jedis("127.0.0.1", 6379);
new Thread(() -> {
    for (int i = 0; i < 1000; i++) {
        String result = jedis.get("a");
        if (!"1".equals(result)) {
            log.warn("Expect a to be 1 but found {}", result);
            return;
        }
    }
}).start();
new Thread(() -> {
    for (int i = 0; i < 1000; i++) {
        String result = jedis.get("b");
        if (!"2".equals(result)) {
            log.warn("Expect b to be 2 but found {}", result);
            return;
        }
    }
}).start();
TimeUnit.SECONDS.sleep(5);

Repeated execution produces various errors, such as mismatched values, JedisConnectionException: Unexpected end of stream, and java.io.IOException: Socket Closed.

Write operations interleave, forming illegal Redis commands that cause the server to close the connection.

Even if commands are sent in order, a thread may read the response intended for another thread, leading to data corruption.

Root Cause Analysis

When a Jedis object is reused across threads, its underlying RedisOutputStream is also shared. Multiple threads writing concurrently cannot guarantee that a complete command is written atomically, nor that no other thread writes between the write and the subsequent read. This explains the observed protocol errors and value mismatches.

Fix: Use JedisPool

The proper solution is to obtain Jedis instances from the thread‑safe JedisPool. Declare the pool as a static shared resource and use try‑with‑resources (or explicit close()) to return connections automatically. Adding a shutdown hook ensures the pool is closed when the application exits.

@PostConstruct
public void init() {
    Runtime.getRuntime().addShutdownHook(new Thread(() -> {
        jedisPool.close();
    }));
}

How JedisPool Works Internally

JedisPool

extends JedisPoolAbstract, which in turn extends the abstract Pool class. The Pool holds an Apache Commons GenericObjectPool that performs the actual object reuse. Therefore, JedisPool does not implement its own pooling algorithm; it delegates to GenericObjectPool.

Conclusion

The Jedis API offers both pooled and non‑pooled usage modes. JedisPool provides a thread‑safe connection pool that should be used in multithreaded applications, while a plain Jedis instance is a non‑thread‑safe single connection and must not be shared across threads.

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.

JavaredisConnection PoolJedis
JavaEdge
Written by

JavaEdge

First‑line development experience at multiple leading tech firms; now a software architect at a Shanghai state‑owned enterprise and founder of Programming Yanxuan. Nearly 300k followers online; expertise in distributed system design, AIGC application development, and quantitative finance investing.

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.