目录
多线程并发的安全问题最典型的问题就是超卖问题或者少卖问题,举例来说:一个商品有500件,一个线程循环5次,每次取100,最后的结果应该是商品的库存归零,而在不处理问题且在高并发的情况下,就会出现商品库存不为零或者为负数的情况;因此,需要给减少商品库存这个操作进行"上锁",来避免这种情况的发生;
不过,需要知晓的是,本地锁只能解决单体架构的项目中出现的并发问题,而且还会有几种失效的情况,因此这里只讲基本用法和失效场景;
在Service层的实现类或者控制器方法前面加入synchronized关键字
使用ReentrantLock对象进行上锁和解锁
JVM本地锁一共有三种失效场景,下面一个个的进行演示
在SpringMVC中,默认是单例模式,而如果切换成多例模式,也就是每个请求都对应一个新的对象;则本地锁无法进行上锁操作
使用@Scope注解切换成多例模式
在切换模式时,使用到了proxyMode = ScopedProxyMode.TARGET_CLASS这样一个属性,有兴趣的可以去下面这个链接深入了解:@Scope注解的proxyMode的作用以及如何影响IoC容器的依赖查找-CSDN博客
给方法体添加@Transactional事务注解,也会使本地锁的作用失效,原因如下:
事务注解是通过AOP思想进行赋能的,因此它会在AOP的前置方法前,开启事务,随后进入方法体,流程如下:
A用户 | B用户 |
begin开启事务 | begin开启事务 |
获取锁成功 | |
查询库存: 21 | |
扣减库存: 20 | |
释放锁 | |
获取锁成功 | |
查询库存: 21 | |
commit提交事务 | |
扣减库存: 20 | |
释放锁 | |
commit提交事务 | |
高并发情况下,两名用户同时进行减库存操作,数据库中的库存却只减了1 |
这个问题其实是可以通过修改事务注解的属性来解决的
这里将事务的隔离级别修改为了未提交,所以才可以解决锁失效的问题,但在实际业务中,未提交的数据是不能被读取到的.因此,不能在实际业务中使用该方法
集群部署在实际业务中是很常见的,原理其实和多例模式较为相似,举个例子:
假如有三个服务器,每个服务器都能访问mysql数据库,每个服务器中都会有商品的实体类对象,这样则违背了本地锁的单例模式要求(但实际上单个服务器中还是单例模式);因此,在集群模式中,本地锁会失去其效果
点击Edit Configurations按钮
复制一个SpringBoot项目的运行实例
修改端口号(复制的运行实例使用的是相同的代码)
根据实际情况选择Linux版本还是Windos版本,此处使用的是windows版本;
修改配置文件(conf目录下的nginx.config文件),配置负载均衡
后续如果修改了配置文件,则在安装目录下使用cmd命令打开终端,运行此命令
nginx -s reload
将压测工具中http请求的端口号换成80,即可进行测试,测试后查看数据库发现,商品库存并没有按照预料中的归零,则说明在集群环境下,JVM本地锁失效