You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多线程环境下使用JedisPool创建Jedis实例的异常问题求助

解决JedisPool多线程场景下的连接异常问题

看起来你在JedisPool的多线程使用上踩了两个关键的坑,导致出现了Unexpected end of stream异常,我来帮你分析并解决这个问题:

问题根源分析

  1. try-with-resources的误用
    你在主线程的循环里用了try (Jedis jedis = jedisPool.getResource()),这个语法糖会在try块执行完毕后自动调用jedis.close()——也就是把这个Jedis实例归还给连接池了。但你紧接着把这个已经被归还的实例传给了子线程,子线程再去操作时,这个连接已经被池回收或复用,自然会抛出连接异常。

  2. 错误的资源分配时机
    你试图在主线程提前为每个线程分配Jedis实例,但连接池的设计初衷是让线程在需要使用Redis时自行从池里获取资源,使用完成后再归还。这种提前分配的方式完全违背了连接池的资源管理逻辑,也无法保证多线程下每个线程拿到的是可用的独立实例。

修正后的代码示例

下面是调整后的正确实现,核心是让每个线程自行从连接池获取和归还资源:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.time.Duration;

public class JedisMultiThreadFix {
    private final static JedisPoolConfig poolConfig = buildPoolConfig();
    private static JedisPool jedisPool = new JedisPool(poolConfig, "localhost");

    public static void main(String[] args) throws Exception {
        MyThread[] myThreads = new MyThread[4];
        for (int i = 0; i < myThreads.length; i++) {
            // 把连接池传给线程,而不是提前分配好的Jedis实例
            myThreads[i] = new MyThread(jedisPool, i);
            myThreads[i].start();
        }

        // 等待所有线程执行完成后再关闭连接池,避免线程还在运行时池被关闭
        for (MyThread thread : myThreads) {
            thread.join();
        }
        jedisPool.close();
    }

    private static JedisPoolConfig buildPoolConfig() {
        final JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(128);
        poolConfig.setMaxIdle(128);
        poolConfig.setMinIdle(16);
        poolConfig.setTestOnBorrow(true);
        poolConfig.setTestOnReturn(true);
        poolConfig.setTestWhileIdle(true);
        poolConfig.setMinEvictableIdleTimeMillis(Duration.ofSeconds(60).toMillis());
        poolConfig.setTimeBetweenEvictionRunsMillis(Duration.ofSeconds(30).toMillis());
        poolConfig.setNumTestsPerEvictionRun(3);
        poolConfig.setBlockWhenExhausted(true);
        return poolConfig;
    }

    static class MyThread extends Thread {
        private final JedisPool jedisPool;
        private final int threadId;

        public MyThread(JedisPool jedisPool, int threadId) {
            this.jedisPool = jedisPool;
            this.threadId = threadId;
        }

        @Override
        public void run() {
            // 在线程内部获取Jedis资源,用try-with-resources自动归还
            try (Jedis jedis = jedisPool.getResource()) {
                System.out.println("Thread " + threadId + ": 拿到的Jedis实例 -> " + jedis);
                // 这里执行你的Redis业务操作,示例:
                jedis.set("thread_key_" + threadId, "thread_value_" + threadId);
                String result = jedis.get("thread_key_" + threadId);
                System.out.println("Thread " + threadId + ": 获取到Redis值 -> " + result);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

关键修正点说明

  • 让线程持有连接池引用:不再提前分配Jedis实例,而是让每个线程在自己的执行逻辑里从池里拿资源,保证了实例的独立性和可用性。
  • 线程内使用try-with-resources:确保Redis操作完成后,Jedis实例自动归还给连接池,避免资源泄漏。
  • 主线程等待线程执行完毕再关池:防止子线程还在运行时连接池被关闭,导致后续操作失败。

额外注意事项

  • Jedis实例本身不是线程安全的,绝对不能在多个线程之间共享同一个Jedis实例,这是官方明确强调的。
  • 连接池的核心价值就是帮你管理Jedis实例的创建、复用和回收,遵循"用时取,用完还"的原则才能发挥它的作用。

内容的提问来源于stack exchange,提问作者user3502577

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:53:54