一.JVM本地锁

发布时间:2024年01月22日

目录

1. 概念

2. 基本用法

2.1. 第一种方式

2.2. 第二种方式:

2.3. 使用jmeter压测工具进行测试:

3. 失效场景

3.1. 多例模式

3.2. 事务

3.3. 集群部署

3.3.1. 模拟集群模式

3.3.2. 安装Nginx进行负载均衡

3.3.3. 使用压测工具测试


1. 概念

多线程并发的安全问题最典型的问题就是超卖问题或者少卖问题,举例来说:一个商品有500件,一个线程循环5次,每次取100,最后的结果应该是商品的库存归零,而在不处理问题且在高并发的情况下,就会出现商品库存不为零或者为负数的情况;因此,需要给减少商品库存这个操作进行"上锁",来避免这种情况的发生;

不过,需要知晓的是,本地锁只能解决单体架构的项目中出现的并发问题,而且还会有几种失效的情况,因此这里只讲基本用法和失效场景;

2. 基本用法

2.1. 第一种方式

在Service层的实现类或者控制器方法前面加入synchronized关键字

2.2. 第二种方式:

使用ReentrantLock对象进行上锁和解锁

2.3. 使用jmeter压测工具进行测试:

3. 失效场景

JVM本地锁一共有三种失效场景,下面一个个的进行演示

3.1. 多例模式

在SpringMVC中,默认是单例模式,而如果切换成多例模式,也就是每个请求都对应一个新的对象;则本地锁无法进行上锁操作

使用@Scope注解切换成多例模式

在切换模式时,使用到了proxyMode = ScopedProxyMode.TARGET_CLASS这样一个属性,有兴趣的可以去下面这个链接深入了解:@Scope注解的proxyMode的作用以及如何影响IoC容器的依赖查找-CSDN博客

3.2. 事务

给方法体添加@Transactional事务注解,也会使本地锁的作用失效,原因如下:

事务注解是通过AOP思想进行赋能的,因此它会在AOP的前置方法前,开启事务,随后进入方法体,流程如下:

A用户

B用户

begin开启事务

begin开启事务

获取锁成功

查询库存: 21

扣减库存: 20

释放锁

获取锁成功

查询库存: 21

commit提交事务

扣减库存: 20

释放锁

commit提交事务

高并发情况下,两名用户同时进行减库存操作,数据库中的库存却只减了1

这个问题其实是可以通过修改事务注解的属性来解决的

这里将事务的隔离级别修改为了未提交,所以才可以解决锁失效的问题,但在实际业务中,未提交的数据是不能被读取到的.因此,不能在实际业务中使用该方法

3.3. 集群部署

集群部署在实际业务中是很常见的,原理其实和多例模式较为相似,举个例子:

假如有三个服务器,每个服务器都能访问mysql数据库,每个服务器中都会有商品的实体类对象,这样则违背了本地锁的单例模式要求(但实际上单个服务器中还是单例模式);因此,在集群模式中,本地锁会失去其效果

3.3.1. 模拟集群模式

点击Edit Configurations按钮

复制一个SpringBoot项目的运行实例

修改端口号(复制的运行实例使用的是相同的代码)

3.3.2. 安装Nginx进行负载均衡

Nginx下载链接

根据实际情况选择Linux版本还是Windos版本,此处使用的是windows版本;

修改配置文件(conf目录下的nginx.config文件),配置负载均衡

后续如果修改了配置文件,则在安装目录下使用cmd命令打开终端,运行此命令

nginx -s reload

3.3.3. 使用压测工具测试

将压测工具中http请求的端口号换成80,即可进行测试,测试后查看数据库发现,商品库存并没有按照预料中的归零,则说明在集群环境下,JVM本地锁失效

文章来源:https://blog.csdn.net/java_qianmo/article/details/135711782
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。