nx set 怎么实现的原子性_Redis实现分布式锁

认真写文章,用心做分享。公众号:Java耕耘者  整理的资料也会放在里面。

1. 分布式锁常见条件

(1)互斥性。临界区任一时刻只能被一个客户端的一个线程所执行。
(2)可重入性。获得锁的线程可以重复获得锁。
(3)获取锁和释放锁必须是相同线程
(4)自动释放锁。获取锁线程崩溃没有主动释放锁,锁仍然可以呗其它线程获取。

理解
  • 条件1、2、3要求锁能够记录获取锁的机器+线程
  • 条件2要求锁需要同一个线程加锁次数进行计数
  • 条件4要求锁有过期时间

2. 基于Redis实现分布式锁

2.1 基于Redis实现分布式锁设计(不对锁进行计数)

优缺点

优点:借助redis高性能对的特点,实现高性能分布式锁。
缺点:

加锁逻辑
94400fa688210fb7c95feeff1d2b12f1.png
解锁逻辑
6610d934ab5af2017587484b5b5c3490.png
实现设计
(1)key和value设计
key = lock.{resource};value = {machineId}_{threadId};
(2)加锁和设置超时时间动作

需要保证set动作、expire动作原子性,主要有两种做法:
i. set时同时使用ex和nx

SET key value [EX seconds] [PX milliseconds] [NX|XX]

ii. 使用lua脚本执行set和expire

(3)释放锁操作

需要保证get动作、del动作原子性,依靠lua脚本实现原子性。

3. Redisson实现分布式锁

(1)分布式锁实现(未实现可重入锁和重试机制)
public  V executeReadWriteLuaScript(String luaScript, RScript.ReturnType returnType, List keys,                                      Object[] values) {    RScript rScript = getScript();    return rScript.eval(RScript.Mode.READ_WRITE, luaScript, returnType, keys, values);}public boolean lock(String resource, Object machineId, Object threadId, long expireTime) {    String key = "lock." + resource;    String value = machineId.toString() + "_" + threadId.toString();    return lock(key, value, expireTime);}public boolean lock(String key, String value, long expireTime) {    RBucket bucket = getBucket(key);    return bucket.trySet(value, expireTime, TimeUnit.SECONDS);}public void unlock(String resource, Object machineId, Object threadId) {    String key = "lock." + resource;    String value = machineId.toString() + "_" + threadId.toString();    unlock(key, value);}public void unlock(String key, String value) {    String luaScript = "local value = redis.call('GET', KEYS[1]); " +            "if (value == ARGV[1]) then " +            "    redis.call('DEL', KEYS[1]); " +            "end ";    executeReadWriteLuaScript(luaScript, RScript.ReturnType.VALUE,            Lists.newArrayList(key),            Lists.newArrayList(value).toArray());}
(2)测试代码
@Testpublic void testDistributedLock() {    Long machineId1 = 123L;    String resource = "resource";    Runnable r = () -> {        if (redisServiceWithRetry.lock(resource, machineId1, Thread.currentThread().getId(), 5)) {            try {                System.out.println(Thread.currentThread().getName() + " locked");            } catch (Exception e) {                e.printStackTrace();            } finally {                redisServiceWithRetry.unlock(resource, machineId1, Thread.currentThread().getId());                System.out.println(Thread.currentThread().getName() + " unlocked");            }        } else {            System.out.println(Thread.currentThread().getName() + " fail to lock");        }    };    Thread t1 = new Thread(r);    Thread t2 = new Thread(r);    t1.start();    t2.start();    try {        t1.join();        t2.join();    } catch (InterruptedException e) {        e.printStackTrace();    }}

结果:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值