学习Redis之Jedis的创建,使用,销毁及线程不安全问题的解决

229 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第13天,点击查看活动详情

Jedis的介绍

Jedis是基于java语言的redis客户端,集成了redis的命令操作,同时提供了连接池管理。

Jedis的优点及缺点

Jedis集成了Redis的命令操作,他提供了比较全面的 Redis 操作特性的 API,同时他的最大的门槛是相对其他主流的Redis的Java客户端他的门槛较低,较简单。但值得一提的是,Jedis同步阻塞 IO, ,不支持异步,而且它本身是一种线程不安全的方式,为解决线程不安全的问题需要线程池来进行连接。

Jedis的导入

传统的jar包导入

传统的jar导入并不多赘述,也不推荐。

众所周知,传统Jar包的导入即去对应的网址下载所需的jar包,然后放在指定的目录的下,但这么做有很多的问题与缺点

统jar包导入的缺点

首先要去找到对应的官网下的对应版本的jar包,有时候还会在浏览器主页中遇到很多的广告的网址,要去做一个筛选判断,而且即便找到了对应好的所需jar包的官网,繁重的英文,大量的不知道干什么的文件......

而且即便是找到了正确的资源jar包,较慢的下载速度,较为繁琐的引入方式,让人很难受。

Maven项目管理引入Jedis

这里我们采用Maven项目管理的方式来完成jar包的引入,这里的另一个好处是,传统的项目的创建,比如idea的项目,在eclipse上未必能运行。而采用Maven项目管理的方式,也一定程度上提高了项目的兼容性。同时在Maven项目中的pom.xml中直接导坐标的形式,一定程度上非常非常非常方便。

创建Maven工程基础的教程这里就不多赘述了

pom.xml中的依赖如下:

redis.clients jedis 3.7.0 org.junit.jupiter junit-jupiter 5.7.0 test ``` ```

这里分别引入了测试和redis.clients的jedis的3.7.0版本

通过new的方式使用Jedis

package com;


import Tools.JedisConnectionFactory;
import redis.clients.jedis.Jedis;

import java.util.stream.StreamSupport;

public class JedisDemo {
    private static Jedis jedis;

    public static void connectJedis(){
        jedis = new Jedis("127.0.0.1",6379);
        // password
        // select
        jedis.select(0);
    }

    public static void run(){
        String result = jedis.set("hobby","吃饭");
        System.out.println("result = "+result);
        String hobby = jedis.get("hobby");
        System.out.println("hobby is "+hobby);
    }

    public static void end(){
        if(jedis != null){
            jedis.close();
        }
    }

    public static void main(String[] args) {
        connectJedis();
        run();
        end();
    }
}

这里我们通过new Jedis(...)的方式完成Jedis的实例化。

如何我们分别定义了三个静态方法

  1. connectJedis()
  2. run()
  3. end()

分别是创立连接,执行命令和结束关闭销毁。

connectJedis()函数

new Jedis()中分别指定ip和端口

正常还需要在下一行提供密码,但是因为我们的redis没有设置密码,这里就不演示了。

run()函数

这里我们在run()函数中执行一些Redis的操作。

我们首先执行了set的操作

在redis中 set key value 可以向库中加入一个 键值对。

然后我们定义一个String result 来接收执行的结果。

然后我们下面又get了这个key(获取),

并通过 String hobby 来接受。

end()函数

在结束时,我们做了一个判断,如果jedis不为空(对象创立连接成功),就关闭销毁。

缺点

值得一提的是,Jedis本身是一种线程不安全的创建方式,这样做会导致线程不安全。

使用线程池建立连接

我们在java 目录下创立一个Tools目录,并创建一个 JedisConnectionFactory

代码如下:

package Tools;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class JedisConnectionFactory {
    private static final JedisPool jedisPool;
    static {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        // 最大连接
        jedisPoolConfig.setMaxTotal(8);
        // 最大空闲连接
        jedisPoolConfig.setMaxIdle(8);
        // 最小空闲连接
        jedisPoolConfig.setMinIdle(0);
        // 设置最长等待时间
        jedisPoolConfig.setMaxWaitMillis(200);
        jedisPool = new JedisPool(jedisPoolConfig,"127.0.0.1",6379,1000);
    }
    public static Jedis getJedis() {
        return jedisPool.getResource();
    }
}

我们定义了一个JedisPool 类,然后在JedisConnectionFactory中写了一段静态方法(在类创建时执行一次且只执行一次)

最后写了一个getJedis()的方法用于对外通过反射连接到线程池中。

改变后的使用(JedisDemo)代码如下:

package com;


import Tools.JedisConnectionFactory;
import redis.clients.jedis.Jedis;

import java.util.stream.StreamSupport;

public class JedisDemo {
    private static Jedis jedis;

    public static void connectJedis(){
//        jedis = new Jedis("127.0.0.1",6379);
        jedis = JedisConnectionFactory.getJedis();
        // password
        // select
        jedis.select(0);
    }

    public static void run(){
        String result = jedis.set("hobby","吃饭");
        System.out.println("result = "+result);
        String hobby = jedis.get("hobby");
        System.out.println("hobby is "+hobby);
    }

    public static void end(){
        if(jedis != null){
            jedis.close();
        }
    }

    public static void main(String[] args) {
        connectJedis();
        run();
        end();
    }
}

可以看到,这里我们只是将原本的new 一个Jedis对象换成了通过我们封装在工具包下的工具类JedisConnectionFactory的类的getJedis()方法来以反射的形式创建这个Jedis对象。这样我们就解决了Jedis的线程不去安全的问题。