首先,介绍spring容器中的Bean 作用域有如下5种:
- singleton 单例模式
- prototype 原型模式
- request (WebApplicationContext环境)
- session (WebApplicationContext环境)
- global-session (WebApplicationContext环境)
singleton
在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例。spring默认是singleton。
prototype
每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例,相当于new 对象()。
request
对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效。
session
对于每次HTTP Session,使用session定义的Bean豆浆产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效。
global-session
每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效。
SpringMVC多线程下
一 。使用singleton:
在一般情况下,只有无状态的Bean(Bean里面没有状态属性)才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。
解释说明一下:(有状态就是有数据存储功能。有状态对象(Stateful Bean),就是有实例变量的对象 ,可以保存数据,是非线程安全的。无状态就是一次操作,不能保存数据。无状态对象(Stateless Bean),就是没有实例变量的对象 .不能保存数据,是不变类,是线程安全的。
无状态的Bean适合用不变模式,技术就是单例模式,这样可以共享实例,提高性能。)
如何保证变量安全性
恶劣情况下,如果是有状态的Bean使用singleton作用域,如果该Bean里面的变量不采取处理,就有可能出现数据错乱的情况。所以这时需要用到ThreadLocal,它会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal(仅仅是变量,因为线程同步的问题就是成员变量的互斥访问出问题)。就相当对把每个线程的名字和变量值以键值对的形式存进Map,当对应的线程访问时,以线程名字取出自己的值,如果没有值则创建自己对应的副本。ThreadLocal采用了“空间换时间”的方式。
ThreadLocal 使用范例:
ThreadLocal<Long>startTime = newThreadLocal<Long>(); 定义一个ThreadLocal 变量
startTime.set(System.currentTimeMillis()); 写入值
startTime.get(); 读取值
如何保证对象的安全性
如果是Bean里面有共用的对象,而且对象里面的方法对数值操作。如下代码:
Controllern层
@RequestMapping("/user")
@Controller
Class UserController {
@Resource
UserService userService;
@RequestMapping("/add")
public void testA(User user){
userService.add(user);
}
@RequestMapping("/get")
public void testA(int id){
userService.get(id);
}
}
Service层:
@Service("userService")
Class UserService{
public static Map<Integer,User> usersCache = new HashMap<String,User>();
public void add(User user){
usersCache.put(user.getId(),user);
}
public void get(int id){
usersCache.get(id);
}
}
这个时候如果Controller、Dao、Service 都是singleton单例模式的话,多个线程同时调用add方法时,有可能会发生用户对象被覆盖,再用get(int id)方法的时候有可能取到的对象不一致。
1)这个时候可以使用 Collections 工具类,它里面有个synchronizedMap方法可以同步Map对象。更改代码如下:
static Map<Integer, Users> usersCache = Collections.synchronizedMap(new HashMap<Integer, Users>()