<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
之前一人一單的業務使用的悲觀鎖,在分散式系統下,是無法生效的。
理想的情況下是這樣的:一個執行緒成功獲取互斥鎖,並對查詢訂單並建立訂單,其他執行緒無法干預。它的原理是會有一個鎖監視器,來監聽是誰獲得了鎖。
但是問題就出現在:
分散式系統下,有多個不同的JVM,不同的JVM的環境下,鎖監聽器是有多個的,就會出現有的執行緒在別的執行緒已經拿到鎖的情況下,仍然可以獲取的到鎖。
這個時候,普通的JVM中的鎖就已經不管用了,就需要我們利用分散式鎖 。
就是可以滿足分散式系統或叢集模式下多程序可見並且互斥的鎖。
它的實現原理就是,不同的JVM環境,都來共用一個鎖監視器。這樣就不會導致出現多個執行緒用多把鎖的情況了。
特點:
主要有三種實現方法,我們可以都來進行一個對比。
如下圖:
這裡主要講基於Redis的分散式鎖的實現 。
實現Reids分散式鎖的方法主要就下面兩個步驟:
1. 獲取鎖
獲取鎖的方法已經是老朋友了,就是使用Redis String型別方法中的setnx方法(互斥性)。但是,為了預防redis伺服器宕機的問題,我們要給鎖設定一個超時時間,避免出現死鎖。(非阻塞)
所以,獲取鎖的方式可以使用如下程式碼
SET lock thread1 nx ex 10
lock是鎖的key,thread1 是value,nx就是setnx方法,ex就是設定超時時間
2. 釋放鎖
釋放鎖就簡單了,刪除即可。
del lock
程式碼實現:
需求:定義一個介面,利用Redis實現分散式鎖的功能。
程式碼如下:
介面程式碼:
package com.hmdp.utils; public interface ILock { /** * 嘗試獲取鎖 * @param timeoutSec 鎖的持有時間,過期自動釋放 * @return true代表獲取鎖成功,false代表獲取鎖失敗。 */ boolean tryLock(long timeoutSec); /** * 釋放鎖 */ void unlock(); }
介面實現類:
package com.hmdp.utils; import org.springframework.data.redis.core.StringRedisTemplate; import java.util.concurrent.TimeUnit; /** * @Version 1.0 */ public class SimpleRedisLock implements ILock { //Redis private StringRedisTemplate stringRedisTemplate; //業務名稱,也就是鎖的名稱 private String name; public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) { this.stringRedisTemplate = stringRedisTemplate; this.name = name; } //key的字首 private static final String KEY_PREFIX = "lock:"; @Override public boolean tryLock(long timeoutSec) { //獲取執行緒id,當作set的value long threadId = Thread.currentThread().getId(); Boolean success = stringRedisTemplate.opsForValue() .setIfAbsent(KEY_PREFIX + name, threadId+"", timeoutSec, TimeUnit.SECONDS); return Boolean.TRUE.equals(success); } //釋放鎖 @Override public void unlock() { //刪除key stringRedisTemplate.delete(KEY_PREFIX+name); } }
業務層獲取鎖和釋放鎖(優惠券秒殺業務修改)
package com.hmdp.service.impl; import com.hmdp.dto.Result; import com.hmdp.entity.SeckillVoucher; import com.hmdp.entity.VoucherOrder; import com.hmdp.mapper.VoucherOrderMapper; import com.hmdp.service.ISeckillVoucherService; import com.hmdp.service.IVoucherOrderService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.hmdp.utils.RedisIdWorker; import com.hmdp.utils.SimpleRedisLock; import com.hmdp.utils.UserHolder; import org.springframework.aop.framework.AopContext; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.time.LocalDateTime; /** * <p> * 服務實現類 * </p> * */ @Service public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService { @Resource private ISeckillVoucherService iSeckillVoucherService; @Resource private RedisIdWorker redisIdWorker; @Resource private StringRedisTemplate stringRedisTemplate; @Override public Result seckillVoucher(Long voucherId) { //1.獲取優惠券資訊 SeckillVoucher voucher = iSeckillVoucherService.getById(voucherId); //2.判斷是否已經開始 if (voucher.getBeginTime().isAfter(LocalDateTime.now())){ Result.fail("秒殺尚未開始!"); } //3.判斷是否已經結束 if (voucher.getEndTime().isBefore(LocalDateTime.now())){ Result.fail("秒殺已經結束了!"); } //4.判斷庫存是否充足 if (voucher.getStock() < 1) { Result.fail("庫存不充足!"); } //5.扣減庫存 boolean success = iSeckillVoucherService.update() .setSql("stock = stock-1").eq("voucher_id",voucherId).gt("stock",0) .update(); if (!success){ Result.fail("庫存不充足!"); } Long userId = UserHolder.getUser().getId(); //1.建立鎖物件 SimpleRedisLock lock = new SimpleRedisLock(stringRedisTemplate, "order:" + userId); //2.嘗試獲取鎖 boolean isLock = lock.tryLock(1200); if (!isLock){ //獲取鎖失敗 return Result.fail("一個使用者只能下一單!"); } try { IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy(); return proxy.createVoucherOrder(voucherId); } finally { //釋放鎖 lock.unlock(); } } @Transactional public Result createVoucherOrder(Long voucherId) { Long userId = UserHolder.getUser().getId(); //6.根據優惠券id和使用者id判斷訂單是否已經存在 //如果存在,則返回錯誤資訊 int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count(); if (count > 0) { return Result.fail("使用者已經購買!"); } //7. 建立訂單 VoucherOrder voucherOrder = new VoucherOrder(); //7.1新增訂單id Long orderId = redisIdWorker.nextId("order"); voucherOrder.setId(orderId); //7.2新增使用者id voucherOrder.setUserId(userId); //7.3新增優惠券id voucherOrder.setVoucherId(voucherId); save(voucherOrder); //8.返回訂單id return Result.ok(orderId); } }
到此這篇關於Redis分散式鎖範例分析講解的文章就介紹到這了,更多相關Redis分散式鎖內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45