使用自定義注解實(shí)現(xiàn)redisson分布式鎖
自定義注解實(shí)現(xiàn)redisson分布式鎖
自定義注解
package com.example.demo.annotation; import java.lang.annotation.*; /** * desc: 自定義 redisson 分布式鎖注解 * * @author: 邢陽(yáng) * @mail: xydeveloper@126.com * @create 2021-05-28 16:50 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface Lock { /** * 鎖的key spel 表達(dá)式 * * @return */ String key(); /** * 持鎖時(shí)間 * * @return */ long keepMills() default 20; /** * 沒(méi)有獲取到鎖時(shí),等待時(shí)間 * * @return */ long maxSleepMills() default 30; }
aop解析注解
package com.example.demo.utils; import com.example.demo.annotation.Lock; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.expression.EvaluationContext; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.stereotype.Component; import java.util.Objects; import java.util.concurrent.TimeUnit; /** * desc: 解析 自定義 redisson 分布式鎖注解 * * @author: 邢陽(yáng) * @mail: xydeveloper@126.com * @create 2021-05-28 16:50 */ @Aspect @Component public class LockAspect { @Autowired private RedissonClient redissonClient; /** * 用于SpEL表達(dá)式解析. */ private final SpelExpressionParser spelExpressionParser = new SpelExpressionParser(); /** * 用于獲取方法參數(shù)定義名字. */ private final DefaultParameterNameDiscoverer defaultParameterNameDiscoverer = new DefaultParameterNameDiscoverer(); @Around("@annotation(com.example.demo.annotation.Lock)") public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { Object object = null; RLock lock = null; try { // 獲取注解實(shí)體信息 Lock lockEntity = (((MethodSignature) proceedingJoinPoint.getSignature()).getMethod()) .getAnnotation(Lock.class); // 根據(jù)名字獲取鎖實(shí)例 lock = redissonClient.getLock(getKeyBySpeL(lockEntity.key(), proceedingJoinPoint)); if (Objects.nonNull(lock)) { if (lock.tryLock(lockEntity.maxSleepMills(), lockEntity.keepMills(), TimeUnit.SECONDS)) { object = proceedingJoinPoint.proceed(); } else { throw new RuntimeException(); } } } finally { if (Objects.nonNull(lock) && lock.isHeldByCurrentThread()) { lock.unlock(); } } return object; } /** * 獲取緩存的key * * key 定義在注解上,支持SPEL表達(dá)式 * * @return */ public String getKeyBySpeL(String spel, ProceedingJoinPoint proceedingJoinPoint) { MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature(); String[] paramNames = defaultParameterNameDiscoverer.getParameterNames(methodSignature.getMethod()); EvaluationContext context = new StandardEvaluationContext(); Object[] args = proceedingJoinPoint.getArgs(); for (int i = 0; i < args.length; i++) { context.setVariable(paramNames[i], args[i]); } return String.valueOf(spelExpressionParser.parseExpression(spel).getValue(context)); } }
service中使用注解加鎖使用
/** * desc: 鎖 * * @author: 邢陽(yáng) * @mail: xydeveloper@126.com * @create 2021-05-28 17:58 */ @Service public class LockService { @Lock(key = "#user.id", keepMills = 10, maxSleepMills = 15) public String lock(User user) { System.out.println("持鎖"); return ""; } }
redisson分布式鎖應(yīng)用
分布式架構(gòu)一定會(huì)用到分布式鎖。目前公司使用的基于redis的redisson分布式鎖。
應(yīng)用場(chǎng)景
1.訂單修改操作,首先要獲取該訂單的分布式鎖,能取到才能去操作。lockey可以是訂單的主鍵id。
2.庫(kù)存操作,也要按照客戶+倉(cāng)庫(kù)+sku維護(hù)鎖定該庫(kù)存,進(jìn)行操作。
代碼:
Redisson管理類
public class RedissonManager { ? ? private static RedissonClient redisson; ? ? static { ? ? ? ? Config config = new Config(); ? ? ? ? config.useSentinelServers() ? ? ? ? ? ? ? ? .addSentinelAddress("redis://127.0.0.1:26379","redis://127.0.0.1:7301", "redis://127.0.0.1:7302") ? ? ? ? ? ? ? ? .setMasterName("mymaster") ? ? ? ? ? ? ? ? .setReadMode(ReadMode.SLAVE) ? ? ? ? ? ? ? ? .setTimeout(10000).setDatabase(0).setPassword("123***"); ? ? ? ? redisson = Redisson.create(config); ? ? } ? ? ? /** ? ? ?* 獲取Redisson的實(shí)例對(duì)象 ? ? ?* @return ? ? ?*/ ? ? public static RedissonClient getRedisson(){ return redisson;} }
分布式鎖
import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import java.util.concurrent.TimeUnit; public class DistributedLock { ? ? private static RedissonClient redissonClient = RedissonManager.getRedisson(); ? ? public static boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) { ? ? ? ? RLock lock = redissonClient.getLock(lockKey); ? ? ? ? try { ? ? ? ? ? ? return lock.tryLock(waitTime, leaseTime, unit); ? ? ? ? } catch (InterruptedException e) { ? ? ? ? ? ? return false; ? ? ? ? } ? ? } ? ? public static void unlock(String lockKey) { ? ? ? ? RLock lock = redissonClient.getLock(lockKey); ? ? ? ? lock.unlock(); ? ? } }
測(cè)試類
public class RedissonTest { ? ? public static void main(String[] args) throws Exception{ ? ? ? ? Thread.sleep(2000L); ? ? ? ? for (int i = 0; i < 3; i++) { ? ? ? ? ? ? new Thread(() -> { ? ? ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? ? ? //tryLock,第三個(gè)參數(shù)是等待時(shí)間,5秒內(nèi)獲取不到鎖,則直接返回。 第四個(gè)參數(shù) 30是30秒后強(qiáng)制釋放 ? ? ? ? ? ? ? ? ? ? boolean hasLock = DistributedLock.tryLock("lockKey", TimeUnit.SECONDS,5,30); ? ? ? ? ? ? ? ? ? ? //獲得分布式鎖 ? ? ? ? ? ? ? ? ? ? if(hasLock){ ? ? ? ? ? ? ? ? ? ? ? ? System.out.println("idea1: " + Thread.currentThread().getName() + "獲得了鎖"); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /** ? ? ? ? ? ? ? ? ? ? ? ? ?* 由于在DistributedLock.tryLock設(shè)置的等待時(shí)間是5s, ? ? ? ? ? ? ? ? ? ? ? ? ?* 所以這里如果休眠的小于5秒,這第二個(gè)線程能獲取到鎖, ? ? ? ? ? ? ? ? ? ? ? ? ?* ?如果設(shè)置的大于5秒,則剩下的線程都不能獲取鎖??梢苑謩e試試2s,和8s的情況 ? ? ? ? ? ? ? ? ? ? ? ? ?*/ ? ? ? ? ? ? ? ? ? ? ? ? Thread.sleep(10000L); ? ? ? ? ? ? ? ? ? ? ? ? DistributedLock.unlock("lockKey"); ? ? ? ? ? ? ? ? ? ? } else { ? ? ? ? ? ? ? ? ? ? ? ? System.out.println("idea1: " + Thread.currentThread().getName() + "無(wú)法獲取鎖"); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } catch (Exception e) { ? ? ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? }) .start(); ? ? ? ? } ? ? } }
我們?cè)俅蜷_(kāi)一個(gè)idea,可以把代碼復(fù)制一份。同事啟動(dòng)兩個(gè)RedissonTest ,模擬了并發(fā)操作。
測(cè)試結(jié)果:
idea2: Thread-1獲得了鎖
idea2: Thread-0無(wú)法獲取鎖
idea2: Thread-2無(wú)法獲取鎖
idea1: Thread-2無(wú)法獲取鎖
idea1: Thread-0無(wú)法獲取鎖
idea1: Thread-1無(wú)法獲取鎖
從測(cè)試結(jié)果發(fā)現(xiàn),最后是只能有一個(gè)idea的一個(gè)線程能獲取到鎖。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
springboot接收前端參數(shù)的四種方式圖文詳解
Spring Boot可以通過(guò)多種方式接收前端傳遞的數(shù)據(jù),下面這篇文章主要給大家介紹了關(guān)于springboot接收前端參數(shù)的四種方式,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-11-11IDEA 如何導(dǎo)入別人的javaweb項(xiàng)目進(jìn)行部署
這篇文章主要介紹了IDEA 如何導(dǎo)入別人的javaweb項(xiàng)目進(jìn)行部署,本文給大家分享我的詳細(xì)部署過(guò)程及遇到問(wèn)題解決方法,需要的朋友可以參考下2023-03-03java向多線程中傳遞參數(shù)的三種方法詳細(xì)介紹
但在多線程的異步開(kāi)發(fā)模式下,數(shù)據(jù)的傳遞和返回和同步開(kāi)發(fā)模式有很大的區(qū)別。由于線程的運(yùn)行和結(jié)束是不可預(yù)料的,因此,在傳遞和返回?cái)?shù)據(jù)時(shí)就無(wú)法象函數(shù)一樣通過(guò)函數(shù)參數(shù)和return語(yǔ)句來(lái)返回?cái)?shù)據(jù)2012-11-11SpringBoot在IDEA中實(shí)現(xiàn)熱部署(JRebel實(shí)用版)
這篇文章主要介紹了SpringBoot在IDEA中實(shí)現(xiàn)熱部署(JRebel實(shí)用版),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05