为什么说spring的bean是线程不安全的?看完这个问题你就知道了

问题描述

今天我们组的一位童鞋写了个导入功能,需要累加excel表各中的总数,他本地测试了没问题,但是发到测试,我发现计算的值明显偏大,此时我还没有review他的代码,我心里还纳闷这么个简单的加法运算怎么也会算错,结果打开代码一看,果然犯了个新手都会犯的错误。

问题阐述

废话不多说,直接上代码

@Service
public class InstanceStackServiceImpl implements InstanceStackService {
    private static int total = 0;
    
    @Override
    public void importInstanceStack(Long donationId, MultipartFile multipartFile) throws IOException {
    	//伪代码,对total进行计算
    	total += num;
    	//保存对象信息
        Donation donation = new Donation();
        donation.setId(donationId);
        donation.setTotal(total);
        donation.setInstanceStackStatus(1);
        donation.setTypeNum(isbnSet.size());
        donation.setUpdatedBy(operator.getUsername());
        donation.setInstanceStackEnterTime(new Date());
        donationMapper.updateByPrimaryKeySelective(donation);
    }
}

其实代码一贴出来,大家都能明白问题出在了哪里,他将total值定义为了全局变量,而众所周知,由于spring管理bean默认为单例模式,因此多线程在使用该service类时是线程不安全的,就代表了多个线程会共用里面的全局变量total,从而导致计算不准确(偏大)。

总结

其实,Spring中的Bean是否线程安全,其实跟Spring容器本身无关。Spring框架中没有提供线程安全的策略,因此,Spring容器中在的Bean本身也不具备线程安全的特性。咱们要透彻理解这个结论,我们首先要知道Spring中的Bean是从哪里来的。

Spring中Bean从哪里来的?

在Spring容器中,除了很多Spring内置的Bean以外,其他的Bean都是我们自己通过Spring配置来声明的,然后,由Spring容器统一加载。我们在Spring声明配置中通常会配置以下内容,如:class(全类名)、id(也就是Bean的唯一标识)、 scope(作用域)以及lazy-init(是否延时加载)等。之后,Spring容器根据配置内容使用对应的策略来创建Bean的实例。因此,Spring容器中的Bean其实都是根据我们自己写的类来创建的实例。因此,Spring中的Bean是否线程安全,跟Spring容器无关,只是交由Spring容器托管而已。

那么,在Spring容器中,什么样的Bean会存在线程安全问题呢?回答,这个问题之前我们得先回顾一下Spring Bean的作用域。在Spring定义的作用域中,其中有 prototype( 多例Bean )和 singleton ( 单例Bean)。那么,定义为 prototype 的Bean,是在每次 getBean 的时候都会创建一个新的对象。定义为 singleton 的Bean,在Spring容器中只会存在一个全局共享的实例。基于对以上Spring Bean作用域的理解,下面,我们来分析一下在Spring容器中,什么样的Bean会存在线程安全问题。

Spring中什么样的Bean存在线程安全问题?

我们已经知道,多例Bean每次都会新创建新实例,也就是说线程之间不存在Bean共享的问题。因此,多例Bean是不存在线程安全问题的。

而单例Bean是所有线程共享一个实例,因此,就可能会存在线程安全问题。但是单例Bean又分为无状态Bean和有状态Bean。在多线程操作中只会对Bean的成员变量进行查询操作,不会修改成员变量的值,这样的Bean称之为无状态Bean。所以,可想而知,无状态的单例Bean是不存在线程安全问题的。但是,在多线程操作中如果需要对Bean中的成员变量进行数据更新操作,这样的Bean称之为有状态Bean,所以,有状态的单例Bean就可能存在线程安全问题。

所以,最终我们得出结论,在Spring中,只有有状态的单例Bean才会存在线程安全问题。我们在使用Spring的过程中,经常会使用到有状态的单例Bean,如果真正遇到了线程安全问题,我们又该如何处理呢?

如何处理Spring Bean的线程安全问题?

处理有状态单例Bean的线程安全问题有以下三种方法:

1、将Bean的作用域由 “singleton” 单例 改为 “prototype” 多例。

2、在Bean对象中避免定义可变的成员变量,当然,这样做不太现实,就当我没说。

3、在类中定义 ThreadLocal 的成员变量,并将需要的可变成员变量保存在 ThreadLocal 中,ThreadLocal 本身就具备线程隔离的特性,这就相当于为每个线程提供了一个独立的变量副本,每个线程只需要操作自己的线程副本变量,从而解决线程安全问题。

都已经看到这里了, 相信大家应该已经知道了 Spring中的Bean是否线程安全以及如何处理Bean的线程安全问题。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值