最近在整理线程安全知识的时候发现了无状态对象这个名词,与之相关的叫有状态对象。百度搜索出来95%的内容都是一样的,而且代码残缺不全根本无从理解,想都不用想就知道是ctrl+c过来的。自己查阅各种资料大概理解了,现在整理一下备忘。
网上关于状态的解释还是很明白的。有状态对象(Stateful Bean)即有对应实例的对象,可以用来保存数据。EE开发中最常见的pojo就是有状态对象。无状态对象即不能保存(也可以叫不能修改)数据的,只能由当前线程进行访问,一次性操作,一锤子买卖。
POJO就是最常见的有状态对象,有成员变量(即数据字段),有变量的getter和setter方法,可以用来保存数据。
Spring工厂创建创建出来的Bean,包括controller,service,dao都是单例的,本身并不保存任何数据只是提供了一种操作,因此都是无状态对象。
无状态对象一定是线程安全的!有状态对象不一定是线程不安全的!无状态对象可以在一定操作下变为有状态对象,而且这种改变往往是不经意间造成的。
接下来分情况讨论一下。注意以下所有的类都不是特指的pojo类,UserInfo类完全可以是controller、service、dao等任意一个bean,不要思维定式看到get就认为是pojo,这是理解有状态对象的关键点。
成员变量没有被操作的有状态对象
标题根本看不懂,看代码。
public class UserInfo {
private String name;
public String getName() {
return name;
}
}
一个简单的类,有成员变量name,但是只有get方法没有set方法,所以成员变量的name值是无法被改变的,因此此对象线程安全。
同理,因为值没有被改变,下面的代码也是线程安全的。
public class UserInfo {
private String name = "xiaoming";
public String getName() {
return name;
}
}
总结:static 修饰的成员变量,或者等价于static修饰的成员变量都是线程安全的。虽然这是个有状态对象,但依然是线程安全。
成员变量是无状态对象的有状态对象
还是看不懂,看代码。
public class UserInfo {
private String name = "xiaoming";
private TestFunction testFunction = new TestFunction();
}
public class TestFunction {
public void test() {
int i = 0;
System.out.println("It works!" + i);
}
}
成员变量引用了另一个无状态对象,此时UserInfo仍然线程安全。
线程不安全的有状态对象
这里为了更具体一点用一个controller来进行说明。
@RestController
public class UserInfoController {
@PostMapping("/create")
public void createUser() {
// 调用业务层逻辑
}
}
这是一个无状态的controller,现在是线程安全的。如果我稍加改动:
@RestController
public class UserInfoController {
private int createCount = 0;
@PostMapping("/create")
public void createUser() {
count += 1;
// 调用业务层逻辑
}
}
现在增加了成员变量createCount使得当前controller从原本的无状态对象转为了有状态对象;又因为createUser方法里面对createCount进行了自增操作,使得成员变量的值发生了改变,这种情况下就会出现线程安全问题。
总结
不要在controller、service、dao任何一层的实现类中创建成员变量并用来储存数据,否则web访问时一定会出现各种异常的错误。如确有必要,请使用ThreadLocal。