使用redis实现分布式锁

开发者 2024-9-18 22:30:14 19 0 来自 中国
几种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方式。简单对比:

您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-19 00:23, Processed in 0.140940 second(s), 32 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表