如何看一個類是不是線程安全的?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 變量確定是線程私有的,因此不會被其餘線程拿到,這樣也就保證了線程的安全。