范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

Springboot使用redis的setnx和getset实现并发锁分布式锁

  为什么需要分布式锁
  在日常开发中,很多业务场景必须保证原子性。举几个例子:支付订单的操作,就不允许同一个订单,被同时支付,否则会产生错误的数据。拍卖一套房子的下单操作,商品只有一个,那只能一个人下单成功。
  如果你只有一台服务器,只运行一个Java程序,那么可以使用Java语言自身的一些锁来实现原子性。但如果我们有多台服务器,甚至不同服务器上跑的是不同的语言。那这时候,我们就需要一个跨平台、跨语言的加锁方式。redis就是其中最方便的一种。核心操作和原理
  使用redis实现并发锁,主要是靠两个redis的命令:setnx和getset。setnx的作用是,当一个key不存在的时候,给它赋值。如果key存在或赋值失败,都会返回错误。getset的作用是,先获取一个key的值,然后再给这个key赋新的值,该命令有原子性。
  那我们的设计思路就是:先用setnx初步获取锁(set当前的时间戳),如果取不到,那么有两种可能,要么是锁被其他线程持有,要么是其他线程使用完锁后,没有正确释放。所以这个时候,我们需要验证这个锁是否过期。就是把setnx的值拿出来(一个时间戳),和当前时间戳求差,看看超时没有(超时时间是自己设定的)如果锁超时了,我们需要释放它,让它能重新工作。但第二步的操作,不是原子性的。可能有多个线程发现这个锁过期了,都想释放它。这时候,就需要getset这个原子操作,来保证只有一个线程成功。核心代码@Service public class RedisLockService {      @Autowired     private RedisService redisService;      /**      * 获取一个Redis分布式锁      * @param lockKey    锁的Key,全局不可重复      * @param lockExpire 锁超时时间,单位毫秒      * @return      */     public boolean getLock(String lockKey, long lockExpire) {         String redisKey = BaseCommonConfig.REDIS_LOCK_KEY + lockKey;         if (!redisService.setnx(redisKey, String.valueOf(System.currentTimeMillis()))) {             //没有拿到锁,但有可能是上一个加锁的人忘了释放锁。所以下面验证锁是否超时。             String lockString = redisService.getString(redisKey);             if (lockString == null) {                 //前面setnx时,该值还存在,现在不存在了。要么是自然过期了,要么是被别人删掉,准备重新加锁了。稳妥起见,这里返回false                 return false;             }             long timestamp = Long.parseLong(lockString);             if (System.currentTimeMillis() - timestamp > lockExpire) {                 //锁已经超时                 //先get值,再set值。原子操作,确保不会多个线程进入后面的逻辑                 String oldTimestamp = redisService.setGet(redisKey, String.valueOf(System.currentTimeMillis()));                 if (oldTimestamp != null && oldTimestamp.equals(lockString)) {                     //如果get不到值,或者get到的值不是前面取出来那个了,说明这个锁已经被别的线程占用了。                      //第二次锁竞争成功                     redisService.setExpireMills(redisKey, lockExpire);                     return true;                 } else {                     return false;                 }             } else {                 return false;             }         }         redisService.setExpireMills(redisKey, lockExpire);         return true;     }      /**      * 删除锁,释放锁      *      * @param lockKey      */     public void delLock(String lockKey) {         String redisKey = BaseCommonConfig.REDIS_LOCK_KEY + lockKey;         redisService.del(redisKey);     } }
  上面的代码使用了一个RedisService的类,里面主要是简单封装了一下redis的操作,你可以替换为自己的service。代码如下:@Service public class RedisService {      @Autowired     private StringRedisTemplate stringRedisTemplate;     @Resource     private RedisTemplate redisTemplate;      public Boolean setnx(String key, String value) {         ValueOperations ops = stringRedisTemplate.opsForValue();         return ops.setIfAbsent(key, value);     }      public String setGet(String key, String value) {         return stringRedisTemplate.opsForValue().getAndSet(key, value);     }       public Long incr(String key) {         return stringRedisTemplate.opsForValue().increment(key);     }      public Long incr(String key,long val) {         return stringRedisTemplate.opsForValue().increment(key,val);     }      public Long decr(String key) {         return stringRedisTemplate.opsForValue().decrement(key);     }      public Long decr(String key,long val) {         return stringRedisTemplate.opsForValue().decrement(key,val);     }      public Long setPutString(String key, String value) {         return stringRedisTemplate.opsForSet().add(key, value);     }      public Boolean setExist(String key, String member) {         return stringRedisTemplate.opsForSet().isMember(key, member);     }      public Set setList(String key) {         return stringRedisTemplate.opsForSet().members(key);     }      public Long setSize(String key) {         return stringRedisTemplate.opsForSet().size(key);     }      /**      * 逐渐废弃没有过期时间的Redis put操作      * @param key      * @param value      */     @Deprecated     public void putString(String key, String value) {         stringRedisTemplate.opsForValue().set(key, value);     }      /**      * s为单位。      *      * @param key      * @param value      * @param time      */     public void putString(String key, String value, long time) {         stringRedisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);     }      public Boolean zPutString(String key, String value, long time) {         return stringRedisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS);     }      public void putString(String key, String value, long time, TimeUnit unit) {         stringRedisTemplate.opsForValue().set(key, value, time, unit);     }      public String getString(String key) {         ValueOperations ops = stringRedisTemplate.opsForValue();         return ops.get(key);     }      public void putObject(String key, Object value, long time, TimeUnit unit) {         redisTemplate.opsForValue().set(key, value, time, unit);     }      public Object getObject(String key) {         return redisTemplate.opsForValue().get(key);     }      public Boolean exist(String key) {         return stringRedisTemplate.hasKey(key);     }      /**      * key有效时间      *      * @param key      * @return      */     public Long expire(String key) {         return stringRedisTemplate.getExpire(key, TimeUnit.SECONDS);     }      public Boolean changeExpire(String key, long time) {         return stringRedisTemplate.expire(key, time, TimeUnit.SECONDS);     }      public Boolean del(String key) {         return stringRedisTemplate.delete(key);     }      public void hset(String key, String field, String value) {         HashOperations ops = stringRedisTemplate.opsForHash();         ops.put(key, field, value);     }      public String hget(String key, String field) {         HashOperations ops = stringRedisTemplate.opsForHash();         return ops.get(key, field);     }      public Map hmget(String key) {         HashOperations ops = stringRedisTemplate.opsForHash();         return ops.entries(key);     }      public void hmset(String key, HashMap data) {         HashOperations ops = stringRedisTemplate.opsForHash();         ops.putAll(key,data);     }      public void hdel(String key, String field) {         HashOperations ops = stringRedisTemplate.opsForHash();         ops.delete(key, field);     }      public void sadd(String key, String value) {         stringRedisTemplate.opsForSet().add(key, value);     }      public Boolean setIsMember(String key,String val){         return stringRedisTemplate.opsForSet().isMember(key,val);     }      public Long ttl(String key) {         return stringRedisTemplate.getExpire(key);     }      public void setExpire(String key, long time) {         stringRedisTemplate.expire(key, time, TimeUnit.SECONDS);     }      public void setExpireMills(String key, long time) {         stringRedisTemplate.expire(key, time, TimeUnit.MILLISECONDS);     }      public List mget(List keys) {         return stringRedisTemplate.opsForValue().multiGet(keys);     }  } 交个朋友
  以上代码有任何疑问,可以点击右侧边栏联系作者。收费5毛~交个朋友,欢迎来撩!
  版权声明:《Springboot使用redis的setnx和getset实现并发锁、分布式锁》为CoderBBB作者「ʘᴗʘ」的原创文章,转载请附上原文出处链接及本声明。
  原文链接:https://www.coderbbb.com/articles/2

华为Mate30系列60天出货超700万台,5G版人气爆棚好评如潮近日,华为消费者业务CEO余承东宣布华为Mate30系列手机上市60天,全球发货量突破700万台,同比增长75。作为华为旗下的高端系列产品,60天破700万台的成绩绝对是相当惊艳的告别埋头等车时代智慧电子站牌点亮桐乡10月18日,第六届世界互联网大会前夕,桐乡恒通公交集团联合高德地图正式发布,高德实时公交能力全面落地桐乡市,覆盖475辆公交车,第一期点亮58块电子站牌。届时,用户可以通过高德地高德集团总裁刘振飞今年十一出游用高德就行了2019年国庆节在即,高德地图第三届十一全民出行节也已拉开帷幕。阿里巴巴合伙人高德集团总裁刘振飞表示,面对即将迎来的十一出游高峰,高德将从交通和旅游等各方面全面升级服务,通过发布大京东11。11进程刚过半两大品牌下单金额破百亿近百品牌破亿今年京东11。11着重推出超级百亿补贴千亿优惠的史上最大优惠力度。在这一优惠激发下,无数消费者涌入京东,形成一次次买买买的浪潮,进而带动多个品牌在京东的销售呈现爆发式增长。截至11河南遭遇历史最强降雨,水漫地铁12人死亡,水库坝溃。7月20日,河南多地突发暴雨致灾牵动全中国人的心。河南省防指决定于7月21日将防汛应急响应级别由级提升为级,为省防最高级别。目前,洪灾已造成郑州市区12人死亡,当地已转移避险约10哆啦A梦版天猫精灵见过么创意满满的天猫精灵您了解一下双11马上就要到了,你想好了要入手些啥了没?亲,智能音箱设备您不妨了解一下呀。说到智能音箱设备,天猫精灵百度小米必须拥有姓名。这三大品牌在国内智能音箱市场长期呈现三足鼎立之势。知名增发200亿农业补贴,这背后蕴含着怎样的信息近日,中国政府网发布了政府工作报告,报告中指出,解决好吃饭问题始终是中国人民的头等大事,一定要保障好全中国14亿人的粮食安全。那我们来看一下政府在保障粮食安全促进农业增收方面都有哪晶合物联升级版电子水尺新品发布新品发布科技创新丨人工智能丨传感预警8。18日下午晶合举行了升级版电子水尺新品发布会,根据原有的电子水尺的功能上进行了品质升级和功能完善,在会上对一些可能会出现的问题进行了一系列的自然资源部59号通知矿山尾矿库地勘已列为重点监测对象近日,自然资源部办公厅发布关于近期加强安全生产工作有效防范化解重大安全风险的通知。通知指出,近期一些地方自然灾害和安全生产事故频发,造成重大人员伤亡和财产损失,教训十分深刻,国务院台风烟花即将北上,山东多地发布预警信息这一轮的强降雨,给我国北方地区带来了严重的洪涝灾害。随着主汛期的到来,下一步防治形势非常严峻。就在上午!今年第6号台风烟花在浙江沿海二次登陆!强降雨再次来袭7月25日至29日,浙江五一想在家快乐追剧痛快游戏?华为路由AX3解决家庭WiFi三大顽疾小长假来临,很多朋友选择宅在家。也许是与家人一起追剧,也许是自己独享安静时光。但无论如何,好的家庭WiFi网络环境都是必须的。否则,就会出现各种尴尬全家在客厅追剧的时候,但是智能电
TCL威5日正式来袭!上车TCL巨幕电视稳赚不亏年关将近,各大厂商都趁着这一个节点摩拳擦掌,推出各种花样十足福利满满的优惠活动,特别是如今的彩电圈,内卷竞争激烈,各大家电品牌方也都下血本推出钜惠福利,譬如现在正在进行的TCL超级高灯科技获2021德勤中国明日之星本报讯近日,2021德勤中国高科技高成长50强和德勤中国明日之星榜单在北京揭晓。深圳高灯计算机科技有限公司(以下简称高灯科技)在评选中脱颖而出,获得2021德勤中国明日之星。据了解微信上线新功能语音支持暂停和继续播放,可朗读文字消息,看视频号时可显示新消息微信iOS版今日发布更新,版本号升级到了8。0。17,此次更新除了解决一些已知问题外,也带来了一些新功能。据正观新闻报道,微信此次更新一个比较实用的功能是语音已经支持暂停和继续播放Facebook因用户数据问题在英国面临诉讼报道,Facebook母公司MetaPlatformsInc。(FB)在英国被起诉,面临至少23亿英镑(合31。5亿美元)索赔,理由是该公司有超过4,400万用户的数据被用于牟利。招商证券应用人工智能技术辅助银行流水识别与分析中证网讯(记者吴瞬)近日,招商证券宣布,经过近一年的课题攻关,招商证券成功破解了银行流水识别和分析这一行业难题,成为业内首家实现全格式银行流水智能分析辅助的金融企业。在人工智能的加让技术流变资金流兴业银行支持科创企业出新招依托大数据,打破银行传统信贷评价模式,兴业银行创新推出的技术流专属评价体系,实现对科创企业科技创新能力的精准画像,成为解锁融资难问题的金钥匙。福建省微柏工业机器人有限公司是技术流专数码党的开年好物,罗技虎年限定款礼盒已上线,安排用腻歪了机械键盘和电竞风格的鼠标,就琢磨着来一套颜值高能给氛围组加分,还功能强大的套装,尤其自从用过可以轻松在设备间切换的蓝牙耳机以后,这个功能的优势就深深的被印在需求里了。MXA20220114涨停原因600933hr爱柯迪2021年我国汽车产销结束连续3年下降趋势,新能源汽车销售连续7年位居全球第一1公司产品包括新能源汽车电驱电控系统,应用于大众奔驰宝马等汽车600458hr时Python之测试环境db自动同步分享主题多套测试环境,如何做基线的数据库级别的同步更新?应用场景工作中测试环境有多套时,为保证基础环境配置的一致性,就需要所有测试环境的数据库结构保持一致。例如A需求在beta1环DOS未公开命令dos系统中有许多未公开的命令与参数。我经过收集整理,发现了很多。由于dos的有好几种,所以下面仅以msdos为例,介绍一下常用命令中的未公开的命令与参数。经过比较,我发现在msd小米众多新品发布小米近期发布大量新品,包括手机手表耳机平板电脑等产品。1手机小米12Pro手机8128G4699元,8256G4999元,12256G5399元。小米12Pro12月28日晚10点