概念
線程安全問題一般都是由全局變量及靜態變量引起的。
有狀態
就是有數據存儲功能。有狀態對象(Stateful Bean),就是有實例變量的對象,可以保存數據,是非線程安全的。
無狀態
就是一次操作,不能保存數據。無狀態對象(Stateless Bean),就是沒有實例變量的對象。不能保存數據,是不變類,是線程安全的。
無狀態的Bean適合用不變模式,技術就是單例模式,這樣可以共享實例,提高性能。
有狀態的Bean,多線程環境下不安全,那麼適合用Prototype原型模式。Prototype: 每次對bean的請求都會創建一個新的bean實例。
解決線程安全的方式
線程同步機制 保證同一時間只有一個線程訪問變量
有兩種實現
synchronized關鍵字 jdk自帶
Lock JDK5之後提供 底層原理 CASThreadLocal
ThreadLocal簡介
ThreadLocal爲每一個線程提供一個獨立的變量副本,從而隔離了多個線程對數據的訪問衝突。因爲每一個線程都擁有自己的變量副本,從而也就沒有必要對該變量進行同步了。ThreadLocal提供了線程安全的共享對象,在編寫多線程代碼時,可以把不安全的變量封裝進ThreadLocal。
原理
ThreadLocal保存一個ThreadLocalMap map集合,key爲當前線程
ThreadLocal源碼
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();
}
/**
* Variant of set() to establish initialValue. Used instead
* of set() in case user has overridden the set() method.
*
* @return the initial value
*/
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
/**
* Removes the current thread's value for this thread-local
* variable. If this thread-local variable is subsequently
* {@linkplain #get read} by the current thread, its value will be
* reinitialized by invoking its {@link #initialValue} method,
* unless its value is {@linkplain #set set} by the current thread
* in the interim. This may result in multiple invocations of the
* {@code initialValue} method in the current thread.
*
* @since 1.5
*/
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}