能说说Spring中的Bean是线程安全的吗?

Spring中的Bean默认并非线程安全,Singleton作用域的Bean在多线程环境下可能存在并发问题。若Bean无实例变量或使用线程安全的变量如ConcurrentHashMap,则可能是线程安全的。Prototype和request作用域分别在每次请求和每个HTTP请求时创建新实例。无状态Bean是线程安全的,而有状态Bean需要关注线程安全问题。Spring通过ThreadLocal处理部分Bean以实现线程安全。

答案:绝不可能是线程安全的,不是线程安全的,也不绝对是线程不安全的

Spring容器中的bean可以分为5个范围

  1. singleton:默认,每个容器中只有一个bean的实例
  2. prototype:为每一个bean请求提供一个实例

以下三种99.99%不用

  1. request:为每一个网络请求创建一个实例,在请求完成后,bean会失效并被垃圾回收器回收
  2. session:确保每个session中有一个bean的实例,在session过期后,bean会随之消失
  3. gloal-session

Tomcat中可能多个线程去请求同一个bean对象实例(例如调用同一个Controller),因为spring默认创建的bean是singleton的,都是线程不安全的。如果在spring的Service或是Controller中没放什么实例变量,那么这个时候就没什么问题,最终的效果可能是多个线程并发去访问数据库(正常的逻辑是在业务代码中访问数据库)

如果单实例的变量放的是例如ConcurrentHashMap这种线程安全的数据结构,那么多线程并发访问的话,是线程安全的。

@Service
public class MyServiceImpl implements MyService{
    public void doService(){
        //执行方法    
    }
}

@Controller
public class MyController{
    @Resource
    private MyService myService;
    
    public void doRequest(){
        myService.doService();    
    }
}

【评论区】

1、prototypeh和request有啥区别吗,看上去都是每次请求都会创建一个bean对象?

每次在ApplicationContext上调用getBean方法时,prototype都会创建一个新的实例。而对于 request 、HttpRequest 只创建一个实例。 所以在 HttpRequest 中,如果 getBean 方法在应用程序上被调用两次,那么将只有一个 bean 实例化和重用,而在同一个 HttpReques t中作用域为 Prototype的 bean 将得到两个不同的实例。

2、他人总结

  • 如果单例对象中有非安全的实例变量,多线程访问,肯定是非线程安全的
  • 如果单例对象中使用的是线程安全的实例变量,例如 CurrentHashMap之类的,那么就算多线程访问,也是线程安全的
  • 如果单例对象中没有实例变量,就是一些方法,那么就不会有多线程导致的并发问题

3、“大部分的应用中,各个bean都是无状态的,因此不需要额外特别关心是否安全的问题。”bean有状态和无状态是什么意思?有什么区别?

有状态对象(Stateful Bean) :就是有实例变量的对象,可以保存数据,是非线程安全的。每个用户有自己特有的一个实例,在用户的生存期内,bean保持了用户的信息,即“有状态”;一旦用户灭亡(调用结束或实例结束),bean的生命期也告结束。即每个用户最初都会得到一个初始的bean。

无状态对象(Stateless Bean):就是没有实例变量的对象,不能保存数据,是不变类,是线程安全的。bean一旦实例化就被加进会话池中,各个用户都可以共用。即使用户已经消亡,bean 的生命期也不一定结束,它可能依然存在于会话池中,供其他用户调用。由于没有特定的用户,那么也就不能保持某一用户的状态,所以叫无状态bean。但无状态会话bean 并非没有状态,如果它有自己的属性(变量),那么这些变量就会受到所有调用它的用户的影响,这是在实际应用中必须注意的。

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

有状态的bean和无状态的bean的区别

SpringBean线程安全性主要取决于Bean的作用域以及Bean本身是否有状态[^2]。 Bean的作用域只是表示Bean的生命周期范围,对于任何生命周期的Bean都是一个对象,这个对象是否线程安全,得看这个Bean对象本身[^1]。对于Singleton作用域的Bean,由于在整个应用程序中只有一个实例,多个线程可能会同时访问该实例,因此需要特别注意多线程访问的线程安全性。如果该Bean是有状态的,那么在多线程环境下可能会出现线程安全问题;如果是无状态的,通常是线程安全的。 对于Prototype作用域和其他作用域,通常更容易确保线程安全性,因为每个线程都有自己的Bean实例,相当于每次请求Bean就相当于new Bean(),这样每个线程操作的都是独立的对象,不会相互影响,从而保证了线程的安全[^2][^3]。 如果Bean是有状态的,开发人员可以通过改变bean的作用域,把 "singleton" 改为 "prototype" 来保证线程安全。修改spring配置文件,把bean的作用域改为prototype的示例如下: ```xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="counter" class="constxiong.interview.threadsafe.Counter" scope="prototype"/> </beans> ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值