多线程环境下使用JedisPool创建Jedis实例的异常问题求助
解决JedisPool多线程场景下的连接异常问题
看起来你在JedisPool的多线程使用上踩了两个关键的坑,导致出现了Unexpected end of stream异常,我来帮你分析并解决这个问题:
问题根源分析
try-with-resources的误用
你在主线程的循环里用了try (Jedis jedis = jedisPool.getResource()),这个语法糖会在try块执行完毕后自动调用jedis.close()——也就是把这个Jedis实例归还给连接池了。但你紧接着把这个已经被归还的实例传给了子线程,子线程再去操作时,这个连接已经被池回收或复用,自然会抛出连接异常。错误的资源分配时机
你试图在主线程提前为每个线程分配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
相关产品推荐
相关产品推荐

