在 使用Spring框架时,很多时候不知道或者忽视了多线程的问题。因为写程序时,或做单元测试时,很难有机会碰到多线程的问题,因为没有那么容易模拟多线 程测试的环境。但如果不去考虑潜在的漏洞,它就会变成程序的隐形杀手,在你不知道的时候爆发。而且,通常是程序交付使用时,在生产环境下触发,会是很麻烦 的事。

那么Spring Bean在大多数情况下,对象实例(Object)和方法是否线程安全呢?

这就要看如何定义Bean的生命管理和范围(Scope)了,就大多数情况而言,如果不特别注意并采取方法,Spring Bean是非线程安全的。原因是Spring Bean的产生是通过容器实现,而使用反向控制IoC时,注入的对象实例也是与其他线程共享的。

例如

?

1
2
3
4
5
6
7
8
9
10
11
12
public class PersonController{    
    private PersonService personService;    
     
    public void setFirstName(HttpServletRequest request){                    
        personService.setFirstName(request.getParameter("firstname"));    
    }    
     
    public String getFirstName(){             
        return   personService.getFirstName();    
    }
                  
}

当 有两个线程访问PersonController时,一个先调用setFristName来设置firstname, 而另一个线程调用getFirstName就会得到第一个线程所设的值。这是因为,PersonController是缺省情况下为单一实例 (Singleton),而personService也是一个单独的实例被注入到personController里。这种情况下,多线程是不安全的, 除非能够保证每个方法(method)都不会改变所共享的对象的属性或状态。

线程安全的方法

public class PersonController {   private PersonService personServicepublic void setFirstName(HttpServletRequest request){        Person person = new Person();        person.setFirstName();        personService.savePerson(person);    }}

上 面的例子是一种线程安全的写法,例如,线程A访问setFirstName方法,当它执行到person.setFirstName()后,系统决定挂起 这个线程,转向执行B,B做了同样的事情并保存。然后,系统再执行被挂起的A线程,由于A线程有自己的堆栈状态,而且它本身没有与其他线程共享的对象实例 或状态。需要补充说一下,这里假定personService.savePerson()方法是线程安全的,否则,还需要调查personService.savePerson()是否线程安全。

因此,在大多数情况下,spring bean是非线程安全的,或者说,如果你不告诉它如何管理对象或方法的线程安全,那么就会潜在线程安全问题。

spring bean因此给出了以下的线程安全的可用声明(annotation),你可以根据实际情况,分别定义你的类或方法。

单实例 singleton(缺省)

在整个Spring IoC容器里,只有一个bean实例,所有线程共享该实例

原型实例prototype

每次请求都会创建并返回一个新的实例,所有线程都有单独的实例使用,这种方式是比较安全的,但会消耗大量内存和计算资源。

定义bean的xml配置文件

?

1
2
3
4
5
6
<bean id="personService" class="com.test.PersonService" scope="prototype">    
<!-- inject dependencies here as required -->
</bean>
<bean id="personControoler" class="com.test.PersonController">    
    <lookup-method name="getPersonService" bean="personService"/>
</bean>

抽象类

?

1
2
3
4
5
6
7
8
9
public abstract class PersonController {    
    public void setFirstName(HttpServletRequest request){        
        Person person = new Person();        
        person.setFirstName();        
        personService.savePerson(person);    
     }   
      
     public abstract PersonService getPersonService();
 }

这里,每次当类中的getPersonService()方法被调用时,就会得到一个新personService实例。需要注意的是,PersonController变成了抽象类。

请求范围实例request

每当接受到一个HTTP请求时,就分配一个唯一实例,这个实例在整个请求周期都是唯一的。

会话范围实例session

在每个用户会话周期内,分配一个实例,这个实例在整个会话周期都是唯一的,所有同一会话范围的请求都会共享该实例。

全局话范围实例globalsession

这与会话范围实例大部分情况是一样的,只是在使用到portlet时,由于每个portlet都有自己的会话,如果一个页面中有多个portlet而需要共享一个bean时,才会用到。

Request范围和Session范围在高并发性的MVC环境下的使用

从上面得知,在缺省情况下,被添加上声明@Controller的类是单一实例的,因此,你需要特别留意类中的方法与变量的线程安全问题。

而当考虑到并发用户的环境下,有些时候可以使用Reqest范围,例如,你需要为每次用户的请求分配一个新的控制器实例,这通常是面对一些处理逻辑很复杂的请求,需要占用比较多的资源以及反应时间要求较高的情况,比如,网上订票或支付。

?

1
2
3
4
5
6
7
8
9
10
@Controller@Scope("request")
public class OnlinePaymentController {                 
    private OnlinePayment payment;          
    public void pay(Float amount){                    
        creditCheck(user);                    
        payment.pay(amount);                    
        ...                    
        //a lot of processes have to be done here          
    }
}

有些时候,可以考虑使用Session范围,针对每个用户,提供一个实例,应付用户的多次请求,例如,网上购物车。

@Controller
@Scope("session")
public class ShoppingCartController {       private ShoppingCart shoppingCartpublic void checkIn(ShoppingItem item, Integer number){                    shoppingCart.add(item, number);          }          public void checkOut(){                    shoppingCart.sum();          }}

实际生产环境中用的比较多的是单一实例与Session范围,如果能够保证单一实例中的线程安全,那么在性能上是首选的,毕竟,当网站用户量不断增加时,仍然能够用有限的实例来处理多个用户请求是比较划算的。

原文链接:http://my.oschina.net/chooli/blog/363444

转载于:https://blog.51cto.com/chengyou/1747561

Spring Bean 中的线程安全相关推荐

  1. Spring Cloud中Hystrix 线程隔离导致ThreadLocal数据丢失(续)

    前言 上篇文章<Spring Cloud中Hystrix 线程隔离导致ThreadLocal数据丢失>我们对ThreadLocal数据丢失进行了详细的分析,并通过代码的方式复现了这个问题. ...

  2. spring bean中scope=prototype“的作用

    今天写代码时,遇到个问题,问题大概如下:在写一个新增模块,当各文本框等输入值后,提交存入数据库,跳到其它页面,当再次进入该新增页面时,上次输入的数据还存在. 经过检查发现是,spring配置文件中,配 ...

  3. Spring Cloud中Hystrix 线程隔离导致ThreadLocal数据丢失

    在Spring Cloud中我们用Hystrix来实现断路器,Zuul中默认是用信号量(Hystrix默认是线程)来进行隔离的,我们可以通过配置使用线程方式隔离. 在使用线程隔离的时候,有个问题是必须 ...

  4. 如何向Spring Bean 中注入java.util.Properties?

    第一种方法是使用如下面代码所示的标签: <bean id="adminUser" class="com.leon.common.Customer"> ...

  5. spring-bean版本_如何模拟Spring bean(版本2)

    spring-bean版本 大约一年前,我写了一篇博客文章如何模拟Spring Bean . 所描述的模式对生产代码几乎没有侵入性. 正如读者Colin在评论中正确指出的那样,基于@Profile注释 ...

  6. 如何模拟Spring bean(版本2)

    大约一年前,我写了一篇博客文章如何模拟Spring Bean . 所描述的模式对生产代码几乎没有侵入性. 正如读者Colin在评论中正确指出的那样,基于@Profile注释的间谍/模拟Spring b ...

  7. 如何封装Spring bean

    据我所知,Spring Framework除了具有单独的上下文之外,没有提供任何封装Spring bean的机制. 因此,当您在Spring的Inversion of Control容器中注册了公共类 ...

  8. Spring精华问答 | Spring Bean的自动装配是怎么回事?

    戳蓝字"CSDN云计算"关注我们哦! Spring框架是由于软件开发的复杂性而创建的.Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情.然而,Sprin ...

  9. 修改java bean,java – 以编程方式修改Spring bean

    我目前正在开发一个Web应用程序.我知道Spring的一些基础知识,但是我并没有那么多地使用它,因此我决定提高我的Spring技能,而且我遇到了一个问题,我无法找到一个好的解决方案. 我想在我的应用程 ...

最新文章

  1. 客户端网页编程,第二章思维导图
  2. 金融行业容器平台落地路径:敏捷响应业务更迭
  3. 给一个元素插入一段HTML
  4. CF1338D:Nested Rubber Bands(树形dp)
  5. windows怎么将图片变为单色图片_印刷丨单色黑与四色黑
  6. Android系统(45)--Monkey 测试相关知识
  7. 发现Tensorflow
  8. Warning: 'https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/' already解决
  9. MySQL 基础系列篇
  10. linux网络适配器驱动程序怎么安装,如何安装网卡驱动_如何重新安装网卡驱动
  11. 部分英文常用口语单词(30%转贴+70%原创)
  12. Java 模拟斗地主发牌
  13. Matplotlib输出中文显示问题
  14. 30 个Python代码实现的常用功能(附案例源码)
  15. 交易中的 “道“ 与 “术“
  16. 【Hadoop Summit Tokyo 2016】领英:4亿会员的数据赋能之旅
  17. 《Pro SQL Server Internals, 2nd edition》CHAPTER 3 Statistics
  18. Linux的历史----Linux内核剖析(一)
  19. 【真人手指动画制作软件】万彩手影大师教程 | 如何在2个动作之间添加新动作
  20. C语言课程设计——N-S图

热门文章

  1. 小脑过度活跃,会引起整个大脑的问题
  2. 感觉皮层实质性参与工作记忆的信息保存
  3. 高德纳咨询公司(Gartner)预测:2019年七大人工智能科技趋势
  4. 中科院陆汝钤获吴文俊人工智能最高成就奖,百度王海峰获吴文俊人工智能杰出贡献奖...
  5. 科学革命与科学教科书
  6. 苹果被罚3.1635亿元,因不愿开放第三方支付!
  7. 关于redis的几件小事(三)redis的数据类型与使用场景
  8. POJ-3662 Telephone Lines 二分+双端队列
  9. golang:mime.Decode、mime.DecodeHeader
  10. Python学习笔记十 IO编程