Spring单例Bean与多线程深度分析

 

接触过Spring框架的同学们都知道Spring的bean默认是单例的,在高并发下,如果在spring的单例bean中设置局部变量,则会发生并发问题。

最近在进行开发时,错误的在单例的bean中使用了成员变量,导致多个线程大并发访问时,出现赋值错误及日志打印混乱的问题。

本文就对单例bean及多线程安全的问题做一次较为深入的探讨,也是对自我的一次反省,之后的开发中,杜绝此类问题,修正开发习惯。

首先我们回顾一下单例模式的概念。单例模式的意思是只有一个实例,例如在Spring容器中某一个类只有一个实例,而且自行实例化后并项整个系统提供这个实例,这个类称为单例类。

当多个用户同时请求一个服务时,容器会给每一个请求分配一个线程,这时多个线程会并发执行该请求对应的业务逻辑(成员方法),此时就要注意了,如果该处理逻辑中有对单例状态的修改(体现为该单例的

成员属性),则必须考虑线程同步问题。

一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。

那么如何提升bean的线程安全呢?

简单的说,若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;

若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。

更进一步划分:

常量始终是线程安全的,因为只存在读操作。

每次调用方法前都新建一个实例是线程安全的,因为没有访问共享的资源。

局部变量是线程安全的。因为每执行一个方法,都会在独立的空间创建局部变量,它不是共享的资源。局部变量包括方法的参数变量和方法内变量。这也就是我们常说的方法封闭。

如果实例无状态,则是线程安全的。如果实例中存在对同一个值的不同的操作行为,或者值在不同线程中都会变,那么就需要注意,不要使用成员变量存储属性。

这里我们引入无状态bean和有状态bean。

有状态就是有数据存储功能。有状态对象(Stateful Bean),就是有实例变量的对象,可以保存数据,是非线程安全的。在不同方法调用间不保留任何状态。

无状态就是一次操作,不能保存数据。无状态对象(Stateless Bean),就是没有实例变量的对象 .不能保存数据,是不变类,是线程安全的。

在spring中无状态的Bean适合用不变模式,就是单例模式,这样可以共享实例提高性能。有状态的Bean在多线程环境下不安全,适合用Prototype原型模式。

Prototype: 每次对bean的请求都会创建一个新的bean实例。

 

总结

为避免发生线程安全问题,在开发和设计系统的时候注意下一下三点:

自己写公用类的时候,要对多线程调用情况下的后果在注释里进行明确说明

对线程环境下,对每一个共享的可变变量都要注意其线程安全性

我们的类和方法在做设计的时候,要尽量设计成无状态的

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值