答案1:
如果一个类设计成单例模式, 那么就要注意他的全局变量,如果变量这里没有问题的话方法的调用也不会存在安全问题,因为线程会同时调用同一个对象的方法,但是在内存中有自己独立的空间作为存储,Spring设计成单例Bean主要是为了节省没必要的资源浪费。
那么出现这种问题如何解决呢?
第一种方式:
既然是全局变量惹的祸,那就将全局变量都编程局部变量,通过方法参数来传递。
第二种方式:
jdk提供了java.lang.ThreadLocal,它为多线程并发提供了新思路。 (当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本)
答案2:
因为成员变量是存放在堆内存中,而堆内存又是线程共享的,这就造成了线程安全问题
因为Spring中的Bean默认是单例的,所以在定义成员变量时也有可能会发生线程安全问题
单例的bean 多线程共享,存在资源竞争。如果单例bean 只关注于方法,不会对Bean的成员执行查询以外的操作,这个bean是线程安全的。 重点在于有无对bean 属性的查询以外操作。
Spring容器中的Bean是否线程安全,容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。
Spring 的 bean 作用域(scope)类型
线程安全这个问题,要从单例与原型Bean分别进行说明。
原型Bean 对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。
单例Bean 对于单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争。
如果单例Bean,是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。
对于有状态的bean,Spring官方提供的bean,一般提供了通过ThreadLocal去解决线程安全的方法,比如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等。 注: Spring容器本身并没有提供线程安全的策略,因此是否线程安全完全取决于Bean本身的特性。
使用ThreadLocal的好处 使得多线程场景下,多个线程对这个单例Bean的成员变量并不存在资源的竞争,因为ThreadLocal为每个线程保存线程私有的数据。