MVC多线程安全问题

成员变量数据复用问题

在实际互联网的访问环境下,表现层会接收来自多个用户的访问请求,但是mvc默认是单例模式的,进入的都是同一个单例的Controller对象,并对此成员变量的值进行修改操作,因此会互相影响,无法达到并发安全

 

 

多线程的并发不安全性

我们会发现多次访问此url,currentNum是自增的

 

使用Scope注解

解决方案

单例模式改原多例模式

对web项目,可以Controller类上加注解@Scope("prototype")@Scope("request"),对非web项目,在Component类上添加注解@Scope("prototype")

 

 

使用ThreadLocal类

ThreadLocal叫做线程变量

ThreadLoacl中填充的变量属于当前线程,该变量对于其它线程而言是隔离的,ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量,如此就不存在多线程数据共享的问题

一句话理解ThreadLocal,threadlocl是作为当前线程中属性ThreadLocalMap集合中的某一个项的key值(threadlocl,value),虽然不同的线程之间threadlocal这个key值是一样,但是不同的线程所拥有的ThreadLocalMap是独一无二的,也就是不同的线程间同一个ThreadLocal(key)对应存储的值(value)不一样,从而到达了线程间变量隔离的目的,但是在同一个线程中这个value变量地址是一样的

 

 

发送30次url请求进行测试

 

 

推荐使用局部变量

使用局部变量后就不会产生因单例模式而导致的数据复用问题,在使用前局部变量会被创建出来,在方法区方法执行完毕后局部变量会交由GC机制自动回收

 

使用并发安全的类

Java作为功能性超强的编程语言,API丰富,如果非要在单例bean中使用成员变量,可以考虑使用并发安全的容器,如ConcurrentHashMapConcurrentHashSet等,将我们的成员变量(一般可以是当前运行中的任务列表等这类变量)包装到这些并发安全的容器中进行管理即可

 

 

分布式或微服务的并发安全

如果还要进一步考虑到微服务或分布式服务的影响,方式4便不足以处理了,所以可以借助于可以共享某些信息的分布式缓存中间件如Redis等,这样即可保证同一种服务的不同服务实例都拥有同一份共享信息(如当前运行中的任务列表等这类变量)

 

 

mvc的线程安全问题

需求:请求提交用户名,后端将用户名数据封装到成员变量username中,然后返回给前端并回显

模拟用户1在请求时的网络延迟(10000ms)

对用户2请求不做网络延迟处理

此时会出现线程安全问题,由于mvc的单例模式,用户2的username信息会覆盖之前用户1的username信息

用户1请求处理完逻辑,之前存入的username值被覆盖为user2,出现线程安全问题

下面是代码模拟

 

此时我们必须要使用成员变量因此无法使用局部变量的方法来解决

解决方案

使用ConcurrentHashMap

 

使用线程同步代码块解决并发问题

 

以下使用hashMap来模拟高并发,出现了线程安全问题,在结果数据中出现了0,1,2

使用线程同步代码块对会发生线程安全问题的代码上锁

 

 

 

总结

在mvc框架中对每个前端请求都会开放一个线程来处理本次请求,并且支持异步处理,在高并发的场景下

 

jmeter压力测试

jmeter官网:https://jmeter.apache.org/

运行bin目录下的jmeter.bat 打开图形界面

 

模拟高并发

1、右键Test plan

2、选择添加 > 线程 > 线程组

 

设置线程

线程数:请求并发数

Ramp-Up时间:请求间隔时间

循环次数:循环请求次数

 

建新http请求

1、右键线程组

2、选择添加 > 取样器 > http请求

 

设置请求ip,端口,路径,执行任务模拟高并发