Spring的并发问题——有状态Bean和无状态Bean

简介: Spring的并发问题——有状态Bean和无状态Bean,这个是一个面试常见的问题,现在总结一下提供给需要的同学

 

**一、有状态和无状态**

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

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

image.gif编辑

package com.sw;

public class TestManagerImpl implements TestManager{

   private User user;    //有一个记录信息的实例

   public void deleteUser(User e) throws Exception {

       user = e ;           //1

       prepareData(e);

   }

   public void prepareData(User e) throws Exception {

       user = getUserByID(e.getId());            //2

       .....

       //使用user.getId();                       //3

       .....

       .....

   }    

}

```

**二、解决有状态bean的线程安全问题**

Spring对bean的配置中有四种配置方式,我们只说其中两种:singleton单例模式、prototype原型模式。

```

<bean id="testManager" class="com.lagou.TestManagerImpl" scope="singleton" />

<bean id="testManager" class="com.lagou.TestManagerImpl" scope="prototype" />

```

默认的配置是**singleton**。

singleton表示该bean全局只有一个实例。

prototype表示该bean在每次被注入的时候,都要重新创建一个实例,这种情况适用于有状态的Bean。

如果对有状态的bean使用了singleton的话会出现线程安全问题。

例如上面的例子,如果有两个用户同时访问,假定为user1,user2

当user1 调用到程序中的1步骤的时候,该Bean的私有变量user被付值为user1

当user1的程序走到2步骤的时候,该Bean的私有变量user被重新付值为user1_create

理想的状况,当user1走到3步骤的时候,私有变量user应该为user1_create;

但如果在user1调用到3步骤之前,user2开始运行到了1步骤了,由于单态的资源共享,则私有变量user被修改为user2

这种情况下,user1的步骤3用到的user.getId()实际用到是user2的对象。

对于这种情况我们可以这样解决

1.将有状态的bean配置成prototype模式,让每一个线程都创建一个prototype实例。但是这样会产生很多的实例消耗较多的内存空间。

2.使用ThreadLocal变量,为每一条线程设置变量副本。

   使用ThreadLocal的例子:

   例如,我们有一个银行的BankDAO类和一个个人账户的PeopleDAO类,现在需要个人向银行进行转账,在PeopleDAO类中有一个账户减少的方法,BankDAO类中有一个账户增加的方法,那么这两个方法在调用的时候必须使用同一个Connection数据库连接对象,如果他们使用两个Connection对象,则会开启两段事务,可能出现个人账户减少而银行账户未增加的现象。使用同一个Connection对象的话,在应用程序中可能会设置为一个全局的数据库连接对象,从而避免在调用每个方法时都传递一个Connection对象。问题是当我们把Connection对象设置为全局变量时,你不能保证是否有其他线程会将这个Connection对象关闭,这样就会出现线程安全问题。解决办法就是在进行转账操作这个线程中,使用ThreadLocal中获取Connection对象,这样,在调用个人账户减少和银行账户增加的线程中,就能从ThreadLocal中取到同一个Connection对象,并且这个Connection对象为转账操作这个线程独有,不会被其他线程影响,保证了线程安全性。

[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0);)

```

public class ConnectionHolder {

   

   public static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {

   };

   

   public static Connection getConnection(){

       Connection connection = connectionHolder.get();

       if(null == connection){

           connection = DriverManager.getConnection(DB_URL);

           connectionHolder.set(connection);

       }

       return connection;

   }

 

}

```

   对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而 ThreadLocal 采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

相关文章
|
3月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
14天前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
1月前
|
XML Java 数据格式
Spring从入门到入土(bean的一些子标签及注解的使用)
本文详细介绍了Spring框架中Bean的创建和使用,包括使用XML配置文件中的标签和注解来创建和管理Bean,以及如何通过构造器、Setter方法和属性注入来配置Bean。
69 9
Spring从入门到入土(bean的一些子标签及注解的使用)
|
2月前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
214 24
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
|
1月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
2月前
|
XML Java 数据格式
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
Spring 第二节内容补充 关于Bean配置的更多内容和细节 万字详解!
218 18
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
|
2月前
|
XML Java 数据格式
spring复习02,xml配置管理bean
详细讲解了Spring框架中基于XML配置文件管理bean的各种方式,包括获取bean、依赖注入、特殊值处理、属性赋值、集合类型处理、p命名空间、bean作用域及生命周期和自动装配。
spring复习02,xml配置管理bean
|
1月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细解析Spring Bean的生命周期及其核心概念,并深入源码分析。Spring Bean是Spring框架的核心,由容器管理其生命周期。从实例化到销毁,共经历十个阶段,包括属性赋值、接口回调、初始化及销毁等。通过剖析`BeanFactory`、`ApplicationContext`等关键接口与类,帮助你深入了解Spring Bean的管理机制。希望本文能助你更好地掌握Spring Bean生命周期。
82 1
|
1月前
|
Java Spring
获取spring工厂中bean对象的两种方式
获取spring工厂中bean对象的两种方式
40 1
|
1月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细介绍了Spring框架中的核心概念——Spring Bean的生命周期,包括实例化、属性赋值、接口回调、初始化、使用及销毁等10个阶段,并深入剖析了相关源码,如`BeanFactory`、`DefaultListableBeanFactory`和`BeanPostProcessor`等关键类与接口。通过理解这些核心组件,读者可以更好地掌握Spring Bean的管理和控制机制。
86 1
下一篇
无影云桌面