业务场景:商品秒杀减库存操作
一、先简单创一个springBoot项目
pom.xml加入依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.dengfeng</groupId> <artifactId>webprojectDemo-springboot</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <java.version>1.8</java.version> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.0.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 热部署 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.6.5</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.8.1</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>2.2.5.RELEASE</version> </dependency> </dependencies> </project>
创建配置文件application.yml
server: port: 8100
新建2个类,IndexController,和启动类StartApplication
StartApplication
package com.dengfeng.lock; import org.redisson.Redisson; import org.redisson.config.Config; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class StartApplication { public static void main(String[] args) { SpringApplication.run(StartApplication.class, args); } //实例化一个redisson客户端,redisson框架主要解决分布式锁时间设置问题 @Bean public Redisson redisson() { Config config=new Config(); config.useSingleServer().setAddress("redis://localhost:6379").setDatabase(0); return (Redisson)Redisson.create(config); } }
IndexController
package com.dengfeng.lock; import java.util.UUID; import java.util.concurrent.TimeUnit; import org.redisson.Redisson; import org.redisson.api.RLock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /* * 使用redisson实现分布式锁 */ @RestController public class IndexController { private static final Logger logger = LoggerFactory.getLogger(IndexController.class); @Autowired private Redisson redisson; @Autowired private StringRedisTemplate stringRedisTemplate; @RequestMapping("/getStock") public String deductStock() { String lockKey="lockKey"; RLock redissonLock = redisson.getLock(lockKey); int stock; try { //synchronized锁是锁在jvm,集群环境下不适用,redisson锁在redis,redis底层是单线程 //加锁redissonLock.lock,底层设置了锁,默认时间30秒,stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "dengfeng",30, TimeUnit.SECONDS); =jedis.setnx(key,value);无key添加成功,有key不能添加 //底层开启了定时续命分线程。加锁后,只有一条线程能执行以下业务代码,其他线程会等待该线程释放资源,会在当前位置阻塞 //分线程每隔10秒检查是否还持有锁,如果持有则延长锁的时间,间隔10秒不是固定,取锁时间的1/3 //底层用lua脚本判断是否有相同key,锁时间,加锁其实就是设置key,value redissonLock.lock(); //高并发多条线程进来时, stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock")); if(stock>0) { int realStock=stock-1; stringRedisTemplate.opsForValue().set("stock", realStock+""); System.out.println("redis数据库扣减成功:库存剩余"+realStock); }else { System.out.println("库存不足,扣减失败"); } } finally{ redissonLock.unlock(); //释放锁 } return String.valueOf(stock); } @RequestMapping("/dengfeng") public String deductStock1() { return "dengfeng123456"; } }
以上代码加锁完成,下面是原理图和底层代码
加锁原理图如下
底层采用lua脚本将字符串返回给redis,使用redis原子性,要么都成功,要么都不成功
附:AOP原理加锁
为了不影响原有业务和代码冗余等,我想通过注解+AOP使用redisson加锁,在每个接口上通过如下注解
参考文章:https://www.cnblogs.com/qq1728209643/p/10050191.html