Spring Bean 中的线程安全
2016-03-04 14:13
453 查看
在
使用Spring框架时,很多时候不知道或者忽视了多线程的问题。因为写程序时,或做单元测试时,很难有机会碰到多线程的问题,因为没有那么容易模拟多线
程测试的环境。但如果不去考虑潜在的漏洞,它就会变成程序的隐形杀手,在你不知道的时候爆发。而且,通常是程序交付使用时,在生产环境下触发,会是很麻烦
的事。 那么Spring Bean在大多数情况下,对象实例(Object)和方法是否线程安全呢?这就要看如何定义Bean的生命管理和范围(Scope)了,就大多数情况而言,如果不特别注意并采取方法,Spring Bean是非线程安全的。原因是Spring Bean的产生是通过容器实现,而使用反向控制IoC时,注入的对象实例也是与其他线程共享的。 例如?
当
有两个线程访问PersonController时,一个先调用setFristName来设置firstname,
而另一个线程调用getFirstName就会得到第一个线程所设的值。这是因为,PersonController是缺省情况下为单一实例
(Singleton),而personService也是一个单独的实例被注入到personController里。这种情况下,多线程是不安全的,
除非能够保证每个方法(method)都不会改变所共享的对象的属性或状态。 线程安全的方法
上
面的例子是一种线程安全的写法,例如,线程A访问setFirstName方法,当它执行到person.setFirstName()后,系统决定挂起
这个线程,转向执行B,B做了同样的事情并保存。然后,系统再执行被挂起的A线程,由于A线程有自己的堆栈状态,而且它本身没有与其他线程共享的对象实例
或状态。需要补充说一下,这里假定personService.savePerson()方法是线程安全的,否则,还需要调查personService.savePerson()是否线程安全。因此,在大多数情况下,spring bean是非线程安全的,或者说,如果你不告诉它如何管理对象或方法的线程安全,那么就会潜在线程安全问题。spring bean因此给出了以下的线程安全的可用声明(annotation),你可以根据实际情况,分别定义你的类或方法。
抽象类?
这里,每次当类中的getPersonService()方法被调用时,就会得到一个新personService实例。需要注意的是,PersonController变成了抽象类。 请求范围实例request 每当接受到一个HTTP请求时,就分配一个唯一实例,这个实例在整个请求周期都是唯一的。 会话范围实例session在每个用户会话周期内,分配一个实例,这个实例在整个会话周期都是唯一的,所有同一会话范围的请求都会共享该实例。 全局[b]会话范围实例globalsession[/b]这与会话范围实例大部分情况是一样的,只是在使用到portlet时,由于每个portlet都有自己的会话,如果一个页面中有多个portlet而需要共享一个bean时,才会用到。
有些时候,可以考虑使用Session范围,针对每个用户,提供一个实例,应付用户的多次请求,例如,网上购物车。
实际生产环境中用的比较多的是单一实例与Session范围,如果能够保证单一实例中的线程安全,那么在性能上是首选的,毕竟,当网站用户量不断增加时,仍然能够用有限的实例来处理多个用户请求是比较划算的。
原文链接:http://my.oschina.net/chooli/blog/363444
使用Spring框架时,很多时候不知道或者忽视了多线程的问题。因为写程序时,或做单元测试时,很难有机会碰到多线程的问题,因为没有那么容易模拟多线
程测试的环境。但如果不去考虑潜在的漏洞,它就会变成程序的隐形杀手,在你不知道的时候爆发。而且,通常是程序交付使用时,在生产环境下触发,会是很麻烦
的事。 那么Spring Bean在大多数情况下,对象实例(Object)和方法是否线程安全呢?这就要看如何定义Bean的生命管理和范围(Scope)了,就大多数情况而言,如果不特别注意并采取方法,Spring Bean是非线程安全的。原因是Spring Bean的产生是通过容器实现,而使用反向控制IoC时,注入的对象实例也是与其他线程共享的。 例如?
有两个线程访问PersonController时,一个先调用setFristName来设置firstname,
而另一个线程调用getFirstName就会得到第一个线程所设的值。这是因为,PersonController是缺省情况下为单一实例
(Singleton),而personService也是一个单独的实例被注入到personController里。这种情况下,多线程是不安全的,
除非能够保证每个方法(method)都不会改变所共享的对象的属性或状态。 线程安全的方法
public class PersonController { private PersonService personService public 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配置文件?Request范围和Session范围在高并发性的MVC环境下的使用
从上面得知,在缺省情况下,被添加上声明@Controller的类是单一实例的,因此,你需要特别留意类中的方法与变量的线程安全问题。而当考虑到并发用户的环境下,有些时候可以使用Reqest范围,例如,你需要为每次用户的请求分配一个新的控制器实例,这通常是面对一些处理逻辑很复杂的请求,需要占用比较多的资源以及反应时间要求较高的情况,比如,网上订票或支付。?@Controller @Scope("session") public class ShoppingCartController { private ShoppingCart shoppingCart public void checkIn(ShoppingItem item, Integer number){ shoppingCart.add(item, number); } public void checkOut(){ shoppingCart.sum(); } }
实际生产环境中用的比较多的是单一实例与Session范围,如果能够保证单一实例中的线程安全,那么在性能上是首选的,毕竟,当网站用户量不断增加时,仍然能够用有限的实例来处理多个用户请求是比较划算的。
原文链接:http://my.oschina.net/chooli/blog/363444
相关文章推荐
- 一个jar包里的网站
- 一个jar包里的网站之文件上传
- 一个jar包里的网站之返回对媒体类型
- Spring整合Quartz(JobDetailBean方式)
- Spring整合Quartz(JobDetailBean方式)
- .NET中保证线程安全的高级方法Interlocked类使用介绍
- 模拟Spring的简单实现
- spring+html5实现安全传输随机数字密码键盘
- Spring中属性注入详解
- SpringMVC框架下JQuery传递并解析Json格式的数据是如何实现的
- struts2 spring整合fieldError问题
- spring的jdbctemplate的crud的基类dao
- 读取spring配置文件的方法(spring读取资源文件)
- Spring Bean基本管理实例详解
- java实现简单美女拼图游戏
- 详解Java的Spring框架中的事务管理方式
- 解析Java的Spring框架的BeanPostProcessor发布处理器
- Java开发框架spring实现自定义缓存标签
- java基本教程之线程休眠 java多线程教程