Jedis非线程安全测试以及原因分析

明明server端对数据的读写时单线程,为什么还要用链接池呢?

经过一轮的百度,得知这里面有两个原因:

  • 主要原因:redis的性能瓶颈主要时网络通讯——网络通讯速度比redis处理速度慢许多。单客户端会导致,网络通讯的时间里,redis处于闲暇,无法发挥其处理能力;

  • 不那么主要(个人认为也很主要)原因:jedis非线程安全。但是百度到的帖子和博客,均没有给出靠谱的测试程序去证明这个说法。

在网上看到好多说jedis实例是非线程安全的,然后通过JedisPool连接池去管理实例,在多线程情况下让每个线程有自己的独立的jedis实例,但是都没有说明为啥jedis实例是非线程安全的,自己对线程安全和非线程安全目前还理解不够深刻,下面就剖析一下jedis的源码。

Jedis 通过继承 BinaryJedis 来持有Client对象,Client类继承BinaryClientBinaryClient继承Connection,下面分别介绍各个类主要做什么事情。

  • Connection:主要用于与redis服务器建立Socket连接,并管理连接

  • Protocol:主要用于redis客户端通信协议的处理

  • BinaryClient:是对Connection的包装,将字节数据发送Connection中进行通信协议的编码,同时实现了Redis字节命令的相关接口

  • Client:是对BinaryClient的包装,主要用于处理字符数据到字节数据的转换工作,然后通过BinaryClient的接口来实现后续与Redis服务器的交互,同时实现了Redis字符命令的相关接口

  • BinaryJedis:持有Client对象,Transaction事务对象,Pipeline管道对象,其中与Client是组成关系,强约束,其它两个是分别在使用事务和管道时才会使用到。通过持有的Client来执行相关命令的,此类处理字节命令

  • Jedis:继承BinaryJedis,与Client类似,Jedis处理字符命令,BinaryJedis处理字节命令。

注意:

关于jedis多线程使用单实例的话线程不安全,因为发送命令和获取返回值时使用全局变量RedisOutputStreamRedisInputStream

于是乎就自己试着写一个简单的测试程序去求证,结果感人~下面上测试代码和运行结果:

先说结论吧:jedis确实是非线程安全的。

代码:

public class Test {
     
    public static void main(String[] args) {
        //连接本地的 Redis 服务
        Jedis jedis = new Jedis("localhost");
        //查看服务是否运行
        System.out.println("服务正在运行: "+jedis.ping());
 
        for (int i = 0; i < 2; i++) {
            int finalI = i;
            new Thread(() -> {
                for (int j = 0; j < 10; j++) {
                    jedis.set("a" + finalI, String.valueOf(finalI));
                    System.out.println("a" + finalI + " = " + jedis.get("a" + finalI));
                }
            }).start();
        }
    }
}

结果:

服务正在运行: PONG
a1 = OK
a1 = OK
a0 = 1
a0 = OK
a1 = 1
a1 = OK
a1 = 1
a0 = OK
a0 = OK
a1 = 0
a1 = OK
a0 = 0
a0 = 1
a1 = OK
a1 = OK
a0 = 1
a1 = OK
a0 = 1
a0 = OK
a0 = OK

解析说明:

假设jedis是线程安全的,那么在代码中,输出语句:“System.out.println(“a” + finalI + " = " + jedis.get(“a” + finalI));”的预期输出结果应该是“a0 = 0”或“a1 = 1”。但是实际上控制台中可以看到“a0 = OK”、“a0 = 1”这样神奇的输出。

然后会出现这样输出的具体原因是什么呢——可以看这里:jedis源码分析及jedis实例非线程安全分析(PS:遗憾没联系上博主,侵删。无论如何,感谢该博主的付出)。大概意思就是,jedis的请求流和响应流都是全局变量,当不同的线程在set和get的时候,有可能会出现线程A的set()的响应流,被线程B的get()作为返回了,所以出现了“a0 = OK”的情况。“a0 = 1”同理:线程A的get(“a1”)的响应流被线程B的get(“a0”)返回了。

参考

https://my.oschina.net/u/4339501/blog/3576217
https://my.oschina.net/cyintl/blog/1931778*

原创文章 280 获赞 464 访问量 10万+

猜你喜欢

转载自blog.csdn.net/qq_33709508/article/details/105781839