如何看一个类是否是线程安全的?
由JMM(Java内存模型)我们可以看出,在堆中的变量,如果同时被多个线程操作,就有可能出现线程安全问题(堆中的数据是线程共享的)。
类分为有状态(有成员变量等)和无状态的, 无状态的类肯定是线程安全的, 我们都知道servlet,还有Spring中的bean都是单例的(在上下文中拿到的对象都是同一个),那它们是怎么保证线程安全的呢? 首先一点是bean最好是无状态的,即Dao,Service这些类最好不要有成员变量, 那这种肯定是安全的, 如果有怎么办, Spring是使用ThreadLocal来保证线程安全的。
线程副本
每个线程(Thread)内部都有一个ThreadLocal.ThreadLocalMap, 可以看出ThreadLocalMap是ThreadLocal的一个静态内部类, 我们看一下源码:
从代码中可以看出, ThreadLocalMap 里有一个Entry数组, Entry里有k, v字段, 而k就是当前的ThreadLocal对应, v就是要保存的变量值。
在ThreadLocal中, 使用set, get方法来设置和获取数据, 这两个做了什么呢, 看下源码:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
先从当前线程里拿到ThreadLocalMap, 然后在从map里获取Entry(当前ThreadLocal对应作为key), entry的value值就是要获取的变量值;
看出这里应该就清楚了ThreadLocal是如何保证线程安全的:
1. 每个线程都有一个ThreadLoclMap成员变量(线程副本);
2. ThreadLocal作为ThreadLocalMap的'key'来获取最终变量的值;
每个线程拿到的ThreadLocalMap 变量肯定是线程私有的,所以不会被其他线程拿到,这样也就保证了线程的安全。