如果不出去面试的话,我应该是不会把这两个东西掺和在一块思考。作为一个老老实实写增删改查的javaboy一般在日常开发中不会有思考这个问题的机会。
先说一下结论,springbean是线程不安全的。
虽然平时熟读八股,但是被问到这个看似不相干的两个问题掺在一块,一时还有点反应不过来,今天来分析下开发中经常用到的service bean到底安不安全,要判断这个问题首先要知道线程安全问题是什么。
线程安全性问题
在操作系统中线程的调度完全由CPU决定,当前运行哪个线程会有一些不确定性,可能上一秒运行A线程下一秒就去运行B线程,所以会让代码中产生很多bug。如果认为是因为这样的线程调度才导致代码产生了bug,则认为线程是不安全的。
站在开发者的角度看线程不安全产生的原因:多个线程之间操作同一个数据,至少有一个线程修改这个数据,也就是说线程不安全本质上是多个线程访问共享变量导致变量值不符合预期。
SpringBean的作用域
-
singleton 单例(默认)
-
prototype 多例 如果某个bean被标记为多例,则每次请求使用该对象时,都会创建一个新的bean实例
- singleton 类依赖了prototype类,容器会在singleton 类初始化就会根据依赖关系将prototype类注入。以后的每一次调用singleton bean都是同一个对象,里面的prototype bean也是最初注入的那个,容器再也不会为singleton bean产生新的prototype bean
-
request 每次HTTP请求,使用request定义的Bean都将产生一个新实例,每个HTTP request中的实例都是独立互不影响的的。
-
session 对于每个HTTP Session,使用session定义的Bean都将产生一个新实例。每个HTTP Session中的实例都是独立互不影响的的。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉
在springboot服务中,每个请求都是tomcat的线程池中的一个线程,既然默认是单利模式也就是说多个线程访问的都是同一个bean对象,那就可以说明如果在bean中修改一个共享变量肯定会出现线程安全性问题。
如何使Bean线程安全?
出现线程安全本质上是因为多个线程访问共享变量,那么我们让bean变成线程私有的即可解决这个问题。
-
使用@Scope("prototype")将bean声明为多例模式,每个线程访问都重新生成一个新的对象返回
-
Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态
- ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。