文章目录
成功的秘诀端赖坚毅的决心。—— 英狄兹雷利
问题原因:Redis是单线程的操作,在单线程的操作下不会产生问题,但是在多线程下,对Redis进行GET和SET操作,因为不是原子性操作,所以此时会产生问题。
实现思路:当一个线程对进行操作时,对其进行占位,此时其他线程进来,发现已经被其他线程占用,然后进行等待或放弃操作。
实现代码:
- 使用线程池封装Jedis
public interface JedisCall {
void call(Jedis jedis);
}
public class RedisDemo {
private JedisPool jedisPool;
public RedisDemo(){
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
//设置连接池最大空闲数
config.setMaxIdle(100);
//最大连接数
config.setMaxTotal(300);
//设置最大等待时间 -1表示无限制
config.setMaxWaitMillis(30000);
//开启空闲时间检查有效性
config.setTestOnBorrow(true);
//连接redis
jedisPool = new JedisPool(config, "127.0.0.1", 6379, 30000);
}
public void execute(JedisCall jedisCall) {
//try resource
try (Jedis jedis = jedisPool.getResource()) {
jedisCall.call(jedis);
}
}
}
- 因为有些时候业务操作的时间超过了锁的过期时间,所以需要解决锁超时问题。Lua 脚本可以在 Redis 服务端原子的执行多个 Redis 命令,同时Redis也内置了对Lua的支持,所以引入Lua脚本来解决锁超时的问题。
通过vi创建Lua脚本。
if redis.call("get",KEYS[1])==ARGV[1] then
return redis.call("del",KEYS[1])
else return 0
end
执行命令,给Lua生成sha1校验和,之后调用sha1就可以调用脚本。
cat lua_lock.lua | redis-cli script load --pipe
public class JedisTest {
public static void main(String[] args) {
RedisDemo redisDemo = new RedisDemo();
redisDemo.execute(jedis -> {
//生成uuid作为释放锁标识符
String uuid = UUID.randomUUID().toString();
//设置key作为锁的标志 过期时间为10秒
String key = jedis.set("k1", "v1", new SetParams().nx().ex(10));
if (key != null && "OK".equals(key)){
//获取锁 成功业务操作
System.out.println("success" );
//执行Lua脚本 释放锁
jedis.evalsha("b8059ba43af6ffe8bed3db65bac35d452f8115d8", Arrays.asList("k1"), Arrays.asList(uuid));
}else {
//未获取锁 失败业务操作
System.out.println("fail");
}
});
}
}