几种redis分布式锁实现
一、简单的分布式锁实现
使用下面的下令,实现一个带自动删除的分布式锁
set key value px 毫秒 nx编写两个lua脚本文件
加锁操纵--lock.lua
-- 使用set key value px milliseconds nx 下令实现分布式锁redis.call('set',KEYS[1],ARGV[1],'px',ARGV[2],'nx')开释锁操纵--unlock.lua
-- 比力线程标示与锁的标示是否划一if (redis.call('get',KEYS[1]) == ARGV[1]) then -- 开释锁 return redis.call('del',KEYS[1])endreturn 0文件放在项目标Resource目次下
代码实现
public class RedisLock { private final StringRedisTemplate stringRedisTemplate; private static final DefaultRedisScript<Long> UNLOCK_SCRIPT; private static final DefaultRedisScript<Long> LOCK_SCRIPT; public RedisLock(StringRedisTemplate stringRedisTemplate) { this.stringRedisTemplate = stringRedisTemplate; this.releaseTime = 1000L; } static { UNLOCK_SCRIPT = new DefaultRedisScript<>(); UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua")); UNLOCK_SCRIPT.setResultType(Long.class); LOCK_SCRIPT = new DefaultRedisScript<>(); LOCK_SCRIPT.setLocation(new ClassPathResource("lock.lua")); LOCK_SCRIPT.setResultType(Long.class); } /** * 加锁 * * @param key */ public void myTryLock(String key) { List<String> list = new ArrayList<>(); list.add(LOCK_KEY + ":" + key); Long expireTime = 100000L; String threadId = Thread.currentThread().getName(); // 加锁,逾期时间十秒 stringRedisTemplate.execute(LOCK_SCRIPT, list, threadId,expireTime.toString()); } /** * 开释锁 * * @param key */ public void myUnLock(String key) { List<String> list = new ArrayList<>(); list.add(LOCK_KEY + ":" + key); String threadId = Thread.currentThread().getName(); // 开释锁 stringRedisTemplate.execute(UNLOCK_SCRIPT, list, threadId); }}二、使用map布局实现可重入的分布式锁
编写两个lua脚本文件
加锁操纵--reentrelock.lua
-- 获取可重入锁获取锁的lua脚本local key = KEYS[1]-- 线程标识local threadId = ARGV[1]-- 锁自动开释时间local releaseTime = ARGV[2]-- 检察当火线程的锁是否存在if(redis.call('exists',key) == 0) then -- 不存在,获取锁 redis.call('hset',key,threadId,'1') -- 设置有用期 redis.call('expire',key,releaseTime) return 1end-- 假如当火线程的锁已经存在if(redis.call('hexists',key,threadId) ==1) then -- 给当前的锁重入次数加一 redis.call('hincrby',key,threadId,'1') -- 重新设置有用期 redis.call('expire',key,releaseTime) return 1end-- 阐明锁不是自己的,获取锁失败return 0开释锁操纵--reentreunlock.lua
-- 获取可重入锁的开释锁lua脚本local key = KEYS[1]-- 线程标识local threadId = ARGV[1]-- 锁自动开释时间local releaseTime = ARGV[2]-- 检察当火线程的锁是否是自己持有if(redis.call('hexists',key,threadId) == 0) then -- 不是自己持有,直接返回 return nilend-- 获取重入次数减一后的值local count = redis.call('hincrby',key,threadId,'-1')-- 假如大于0阐明,锁另有重入次数if(count > 0) then -- 重置开释时间 redis.call('expire',key,'10') return nilelse -- 阐明重入次数为0直接删除 redis.call('del',key) return nilend文件放在项目标Resource目次下
代码实现
public class RedisLock { private final StringRedisTemplate stringRedisTemplate; private final Long releaseTime; private static final DefaultRedisScript<Long> REENTRELOCK_SCRIPT; private static final DefaultRedisScript<Long> REENTREUNLOCK_SCRIPT; private final String LOCK_KEY = "lock"; public RedisLock(StringRedisTemplate stringRedisTemplate, Long releaseTime) { this.stringRedisTemplate = stringRedisTemplate; this.releaseTime = releaseTime; } public RedisLock(StringRedisTemplate stringRedisTemplate) { this.stringRedisTemplate = stringRedisTemplate; this.releaseTime = 1000L; } static { REENTRELOCK_SCRIPT = new DefaultRedisScript<>(); REENTRELOCK_SCRIPT.setLocation(new ClassPathResource("reentrelock.lua")); REENTRELOCK_SCRIPT.setResultType(Long.class); REENTREUNLOCK_SCRIPT = new DefaultRedisScript<>(); REENTREUNLOCK_SCRIPT.setLocation(new ClassPathResource("reentreunlock.lua")); REENTREUNLOCK_SCRIPT.setResultType(Long.class); } /** * 获取锁 */ public boolean tryLock() { List<String> list = new ArrayList<>(); String threadId = Thread.currentThread().getName(); list.add(LOCK_KEY + threadId); Long result = stringRedisTemplate.execute(REENTRELOCK_SCRIPT, list, threadId, releaseTime.toString()); return (result != null) && (result == 1L); } /** * 开释锁 */ public void unLock() { List<String> list = new ArrayList<>(); String threadId = Thread.currentThread().getName(); list.add(LOCK_KEY + threadId); stringRedisTemplate.execute(REENTREUNLOCK_SCRIPT, list, threadId, releaseTime.toString()); }}三、直接引入redisson,使用redisson实现可以续时的分布式锁
redisson内里的分布式锁实在就是使用的上面的第二种可重入锁实现的,而且它在内里还实现了watchdog(看门狗)这种可以给锁续逾期时间的机制。在构建锁对象的时间使用无参的构造即可自动启动看门狗机制。
总结
使用redis实现分布式锁的时间,肯定要思量到操纵redis的原子性,假如使用redis的java的api来操纵redis,多条下令会调用多次api,高并发情况下就会造成原子性题目。以是发起直接用lua脚原来实现,由于lua脚本是可以包管原子性的。
其他实现分布式锁的方式另有数据库方式,zookeeper方式。简单对比:
|