如何看一个类是不是线程安全的?java
由JMM(Java内存模型)咱们能够看出,在堆中的变量,若是同时被多个线程操做,就有可能出现线程安全问题(堆中的数据是线程共享的)。数组
类分为有状态(有成员变量等)和无状态的, 无状态的类确定是线程安全的, 咱们都知道servlet,还有Spring中的bean都是单例的(在上下文中拿到的对象都是同一个),那它们是怎么保证线程安全的呢? 首先一点是bean最好是无状态的,即Dao,Service这些类最好不要有成员变量, 那这种确定是安全的, 若是有怎么办, Spring是使用ThreadLocal来保证线程安全的。安全
线程副本this
每一个线程(Thread)内部都有一个ThreadLocal.ThreadLocalMap, 能够看出ThreadLocalMap是ThreadLocal的一个静态内部类, 咱们看一下源码:线程
从代码中能够看出, ThreadLocalMap 里有一个Entry数组, Entry里有k, v字段, 而k就是当前的ThreadLocal对应, v就是要保存的变量值。code
在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是如何保证线程安全的:get
1. 每一个线程都有一个ThreadLoclMap成员变量(线程副本);源码
2. ThreadLocal做为ThreadLocalMap的'key'来获取最终变量的值;
每一个线程拿到的ThreadLocalMap 变量确定是线程私有的,因此不会被其余线程拿到,这样也就保证了线程的安全。