20240302

基于Redis实现分布式锁

1、利用set nx ex获取锁,并设置过期时间,保存线程标识

2、释放锁时先判断线程标示是否与自己一致,一致则删除锁

注意:释放锁时,需将判断与释放这两个操作变成原子性操作,否则仍可能出现误删情况,这种情况如下图所示:

图中线程1在判断锁标识后出现阻塞,并超时释放,释放后线程2能获取锁,当线程1阻塞完成后由于在前面已判断标识的一致性,因此此时线程1能执行释放操作,从而释放线程2,导致误删。

将操作处理为原子操作的方法为使用Lua脚本,其大致形式如下:

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

redis.call函数中有三个参数,第一个为要执行的命令名称,如"get"、"set",第二个为key列表即操作的redis中的key,第三个为其他参数列表,即要执行这条命令所需要的其他参数的列表。要调用这个脚本,则要调用StringRedisTemplate中的execute函数,并传入DefaultRedisScript<T>类型变量、key列表、其他参数(有几个传几个),其中T为执行脚本后返回的数据类型,DefaultRedisScript<T>类型变量初始化如下,使用static代码块保证初始执行时被加载:

private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;
static {
	UNLOCK_SCRIPT = new DefaultRedisScript<>();//初始化
    UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));//指定脚本的位置,ClassPathResource指在resources目录下
    UNLOCK_SCRIPT.setResultType(Long.class);//返回类型
}

execute执行方式如下:

stringRedisTemplate.execute(
                UNLOCK_SCRIPT,
                Collections.singletonList(KEY_PREFIX + name),//Collections.singletonList函数将单个元素转化为List
                ID_PREFIX + Thread.currentThread().getId());//后面可继续跟参数

全部评论

相关推荐

4 9 评论
分享
牛客网
牛客企业服务