springboot单例模式与线程安全问题踩的坑

       最近有客户反映,使用公司产品时,偶尔会存在崩溃情况,自己测试无问题,然后去查日志,是报空指针。于是顺藤摸瓜 往上找,好嘛,之前的开发使用了成员变量,感觉问题就是在这里了,因为众所周知,springboot 采用的是单例模式,所以,使用成员变量时一定要谨慎。下面上一张该类的截图:

springboot单例模式与线程安全问题踩的坑_第1张图片

 大家可能看到了,该类上面加上了@Scope("prototype") 注解,该注解的作用是将该类变成多例模式。讲道理因为变为了多例,应该不会有线程问题了。

我先说下我这边的一个代码环境,上面大家看到的BaseController这个类里面有个init方法,会在继承它的类的所有方法前执行。

springboot单例模式与线程安全问题踩的坑_第2张图片

 使用的是@ModelAttribute注解,这个注解的意思是,在该controller的所有方法前执行,意在初始化,我猜测之前的同事应该是为了获取相同的一些参数,抽调出来做一个父类,随着迭代,别的同事为了方便,拿来就用,导致很多controller继承了该类。

@Scope("prototype")注解:大家设想一下,若父类加了@Scope("prototype")注解,子类controller并没有加该注解,会怎样呢?该注解是否还有意义?再比如,我在某service上加上@Scope("prototype")注解,但调用的controller没有加@Scope("prototype")注解,那么会出现什么样的结果呢?大家可以去测试一下,测试方法也很简单,就是在对应的父类或service的无参构造方法里打印该类的地址。

下面说下我的测试结果:先说父类上加了@Scope("prototype")注解,子类上没有加这种情况。结果是,同一子类继承的为同一父类,不同子类继承为不同父类。理解一下,很简单,因为springboot为单例模式,所以子类为单例,那么只有一个子类,父类肯定是一样的。所以,不同线程过来使用的为同一变量,就会有问题。

同理:在service上标注@Scope("prototype")注解,那在同一个controller里,该service还是同一个,也就是说还是单例的,在不同的controller里 是不同的。测试方法同上。

 现在说下解决方法:1、是在继承该controller的子类上都加上@Scope("prototype")注解。这样做的好处是简单。坏处也同样明显,因为是多例的,那么就会产生大量的实体类,占用大量内存,若是回收不及时,有可能会出现内存溢出。

2、是将变量私有化,比如使用线程变量,对变量加锁等,技术上会复杂一些,而且调试不太好调试。说不定那些地方就会出现问题,毕竟是老代码。

3、将该类转换为拦截器,将变量放入request里,用的时候取出来。

你可能感兴趣的:(springboot)