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

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就可以在多线程中共享了。