Spring问题 之创建好的单例对象是否存在线程安全问题
程序员文章站
2022-05-23 10:18:52
...
Spring的Bean作用域
作用域 | 字符 | 描述 |
---|---|---|
单例 | singleton | 整个应用中只创建一个实例 |
原型 | prototype | 每次注入时都新建一个实例 |
会话 | session | 为每个会话创建一个实例 |
请求 | request | 为每个请求创建一个实例 |
默认的是:单例 singleton
创建单例的方式是否线程安全
懒汉式(不安全写法):
public class Singleton{
private Singleton(){}
private static Singleton singleton = null; //不建立对象
public static Singleton getInstance(){
if(singleton == null) { //先判断是否为空
singleton = new Singleton (); //懒汉式做法
}
return singleton;
}
}
饿汉式(不存在线程安全问题):
public class Singleton{
public Singleton(){}
private static Singleton singleton = new Singleton(); //建立对象
public static Singleton getInstance(){
return singleton;//直接返回单例对象 }
}
懒汉式(安全写法):
public class Singleton{
private Singleton(){}
private static Singleton singleton = null; //不建立对象
public static synchronized Singleton getInstance(){
if(singleton == null) { //先判断是否为空
singleton = new Singleton (); //懒汉式做法
}
return singleton ;
}
}
怎么判断使用已经创建好的单例对象是否线程安全
- 看这个单例里有没有全局变量(全局变量就是成员变量,成员变量又分实例变量和静态变量)
- 如果有全局变量,看它是不是只可以读取而不能写入(有没有发布set方法)
如果满足上面两个条件,那么这个单例就是不安全的。
Spring的单例模式与线程安全
Spring框架里的bean获取实例的时候都是默认单例模式,所以在多线程开发里就有可能会出现线程不安全的问题。当多个用户同时请求一个服务器时,容器(tomcat)会给每一个请求分配一个线程,这时多个线程会并发执行该请求所对应的业务逻辑(controller里的方法),此时就要注意啦,如果controller(是单例对象)里有全局变量并且又是可以修改的,那么就需要考虑线程安全的问题。
解决方案有很多,比如设置@scope(“prototype”)为多例模式,为每个线程创建一个controller,还可以使用ThreadLocal。
ThreadLocal基本实现思路是:它会为每个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突,因为每个线程都拥有自己的变量副本,从而也就没必要对该变量进行同步啦。
在ssh或ssm框架里的service或dao对象虽然也是单例模式,但正如上面分析的,他们没有可修改的全局变量,所以在多线程环境下也是安全的。
有状态对象和无状态对象
无状态对象:无法存储数据,不存在线程安全问题。
有状态对象:有数据存储功能,多线程环境下存在线程安全问题。
其实如果一个有状态对象且没有set方法只是可读,其实也是安全的。