redis分布式锁 java_Redis分布式锁的Java实现

Redis分布式锁算法有两种,一种是单个Redis实例下的,一种是多个Redis实例的Redlock算法。

官方推荐Redlock算法,但是这个算法需要比较多的Redis实例而且是完全互相独立,不存在主从复制或者其他集群协调机制的,所以不太适合小项目。

单Redis实例

原理

某个线程调用Redis命令 SET key value NX PX 30000。

这个命令的意思是,仅在不存在key的时候才能被执行成功(NX选项),并且这个key有一个30秒的自动失效时间(NX选项)。key的失效时间是一个调用线程独占锁的时间。这个key的value最好是一个随机数,value在所有的调用线程中必须是唯一的。value是随机数主要是为了更安全的释放锁,释放锁的时候使用脚本告诉Redis,只有key存在并且存储的值和我指定的值一样才能告诉我删除成功。可以通过以下Lua脚本实现:

if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end。

使用这种方式释放锁可以避免删除别的调用线程获取成功的锁。举个例子:线程A取得资源锁,当线程A运行完毕其他操作后要释放锁时,原来的锁早已超时并且被Redis自动释放,并且在这期间资源锁又被线程B再次获取到。如果仅使用DEL命令将key删除,那么这种情况就会把线程B的锁给删除掉。使用Lua脚本就不会存在这种情况,因为脚本仅会删除value等于线程的value的key。

但是这个方法有个缺点,如果Redis服务器宕机,那么就会导致无法获取锁,后果就是无法执行后续方法。假如采用主从复制,因为Redis主从是异步的,就是master不会等待slave接收了数据再响应客户端。考虑这种情景,线程A请求锁,master写入成功后发送给slave前master宕机,触发故障转移,slave升级为master,正好这时线程B又来获取锁并且成功,那么线程A和B都可以执行任务,所以用这种方式的话,宁愿不要有slave。不过这种属于小概率错误,在一个保证永不宕机的环境下这个方式没有任何问题。

示例

以下Demo类中的调用线程可以假设是分布在多个JVM进程中的线程,为了方便测试,共享数据也是设置到Redis中。

import redis.clients.jedis.Jedis;

/**

* 单实例Redis分布式锁工具类

*/

public class RedisDLUtils {

/**

* 加锁

*

* @param jedis

* @param key

* @param value

* @param seconds

* @return

*/

public static Boolean lock(Jedis jedis, String key, String value, int seconds) {

String result = jedis.set(key, value, "NX", "EX", seconds);

return (result != null) && ("OK".equals(result));

}

/**

* 释放锁

*

* @param jedis

* @param key

* @param value

* @return

*/

public static Boolean unlock(Jedis jedis, String key, String value) {

if (value.equals(jedis.get(key))) {

return jedis.del(key) == 1;

}

return false;

}

/**

* 通过执行Lua脚本释放锁

*

* @param jedis

* @param key

* @param value

* @return

*/

public static Boolean unlockByLua(Jedis jedis, String key, String value) {

String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end";

long result = (long) jedis.eval(script, 1, key, value);

return result == 1;

}

}

import com.ice.util.RedisDLUtils;

import redis.clients.jedis.Jedis;

import redis.clients.jedis.JedisPool;

import redis.clients.jedis.JedisPoolConfig;

import java.util.UUID;

import java.util.concurrent.CountDownLatch;

public class Demo {

public static void main(String[] args) throws Exception {

JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();

jedisPoolConfig.setMinIdle(10);

jedisPoolConfig.setMaxIdle(50);

jedisPoolConfig.setMaxTotal(150);

JedisPool jedisPool = new JedisPool(jedisPoolConfig, "192.168.164.128", 6379);

CountDownLatch countDownLatch = new CountDownLatch(100);

for (int i = 0; i < 100; i++) {

new Thread(() -> {

String uuid = UUID.randomUUID().toString();

String key = "lock";

Jedis jedis = jedisPool.getResource();

try {

boolean result = false;

do {

result = RedisDLUtils.lock(jedis, key, uuid, 30);

} while (result == false);

int value = Integer.parseInt(jedis.get("value"));

jedis.set("value", ++value + "");

} finally {

jedis.close();

// RedisDLUtils.unlock(jedis, key, uuid);

RedisDLUtils.unlockByLua(jedis, key, uuid);

countDownLatch.countDown();

}

}).start();

}

countDownLatch.await();

try (Jedis jedis = new Jedis("192.168.164.128", 6379)) {

int value = Integer.parseInt(jedis.get("value"));

System.out.println("value=" + value);

}

}

}

Redlock算法

......

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值