之前一人一單的業務使用的悲觀鎖,在分散式系統下,是無法生效的。
在最理想的情況下,如果一個執行緒可以成功地取得互斥鎖並對訂單進行查詢和創建,則其他執行緒將無法幹擾。它的原理是會有一個鎖監視器,來監聽是誰獲得了鎖。
但問題就出現在:
分散式系統下,有多個不同的JVM,不同的JVM的環境下,鎖定監聽器是有多個的,就會出現有的線程在別的線程已經拿到鎖的情況下,仍然可以取得的到鎖。
這時候,普通的JVM中的鎖定就已經不管用了,就需要我們利用分散式鎖定 。
就是可以滿足分散式系統或叢集模式下多進程可見且互斥的鎖。
它的實作原理就是,不同的JVM環境,都來共用一個鎖定監視器。這樣就不會導致出現多個執行緒用多把鎖的情況了。
特點:
主要有三種實作方法,我們可以都來進行一個對比。
如下圖:
這裡主要講基於Redis的分散式鎖定的實作 。
實作Reids分散式鎖定的方法主要就下面兩個步驟:
1. 取得鎖定
使用Redis中String類型的setnx方法(保證互斥性)已經是大家熟知的獲取鎖的方式。為了防止redis伺服器崩潰,我們需要為鎖定設定逾時時間,以避免死鎖。 (非阻塞)
所以,取得鎖定的方式可以使用以下程式碼
##SET lock thread1 nx ex 10lock是鎖定的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分散式鎖的原理是什麼和怎麼實現的詳細內容。更多資訊請關注PHP中文網其他相關文章!