ThreadLocal 模式是解決併發數據共享的一個典型的方案,spring,struts等經典框架都有用到ThreadLocal 的場景。
在學習實驗中寫了下面一個例子:
package a;
public class TestLocal {
public static ThreadLocal<User> threadLocal = new ThreadLocal<User>();
public static void set(User u) {
threadLocal.set(u);
}
public static User get() {
return threadLocal.get();
}
public static void main(String[] args) {
final User u = new User();
TestLocal.set(u);
for (int i = 0; i < 20; i++) {
new Thread(new Runnable() {
public void run() {
System.out.println("current thread name :" +Thread.currentThread().getName()+" localuser :" + TestLocal.get());}
}).start(); }
}
}
打印結果:
current thread name :Thread-1 localuser :null
current thread name :Thread-3 localuser :null
current thread name :Thread-0 localuser :null
current thread name :Thread-4 localuser :null
current thread name :Thread-7 localuser :null
current thread name :Thread-2 localuser :null
current thread name :Thread-18 localuser :null
current thread name :Thread-11 localuser :null
current thread name :Thread-6 localuser :null
current thread name :Thread-8 localuser :null
current thread name :Thread-10 localuser :null
current thread name :Thread-12 localuser :null
current thread name :Thread-14 localuser :null
current thread name :Thread-16 localuser :null
current thread name :Thread-5 localuser :null
current thread name :Thread-9 localuser :null
current thread name :Thread-13 localuser :null
current thread name :Thread-15 localuser :null
current thread name :Thread-19 localuser :null
current thread name :Thread-17 localuser :null
由於初始ThreadLocal,對你理解不太深入,剛開始有點迷惑,追其
(ThreadLocal)在set和get時源碼:
/**
* 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);
}
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
顯然,結果跟源碼是一致的。在set方法中,程序先獲得了當前線程實例 (Thread t = Thread.currentThread()),根據實例獲得(ThreadLocalMap map = getMap(t))ThreadLocalMap,如果不存在就根據當前線程創建,存在則直接將變量值以當前ThreadLocal實例爲key保存起來,這樣value就與當前線程綁定了,這也正是ThreadLocal在處理併發共享時的精髓所在。
再回到上面的例子,本人現在主線程將user通過set方法放進了ThreadLocal上,其實這是將user綁定到了主程序,接着到新建的其他子線程裏邊通過get方法去哪,顯然會是得到null的,因爲這個時候的get方法是針對當前子線程了。
針對上面的理解,再一次寫了個例子:
package a;
public class TestLocal {
public static ThreadLocal<User> threadLocal = new ThreadLocal<User>();
public static void set(User u) {
threadLocal.set(u);
}
public static User get() {
return threadLocal.get();
}
public static void main(String[] args) {
final User u = new User();
for (int i = 0; i < 20; i++) {
new Thread(new Runnable() {
public void run() {
TestLocal.set(u);
System.out.println("current thread name :" +Thread.currentThread().getName()+" localuser :" + TestLocal.get());
}
}).start();
}
}
}
控制檯打印結果顯示:
current thread name :Thread-0 localuser :a.User@13c5982
current thread name :Thread-1 localuser :a.User@13c5982
current thread name :Thread-3 localuser :a.User@13c5982
current thread name :Thread-5 localuser :a.User@13c5982
current thread name :Thread-2 localuser :a.User@13c5982
current thread name :Thread-4 localuser :a.User@13c5982
current thread name :Thread-6 localuser :a.User@13c5982
current thread name :Thread-9 localuser :a.User@13c5982
current thread name :Thread-7 localuser :a.User@13c5982
current thread name :Thread-11 localuser :a.User@13c5982
current thread name :Thread-13 localuser :a.User@13c5982
current thread name :Thread-17 localuser :a.User@13c5982
current thread name :Thread-19 localuser :a.User@13c5982
current thread name :Thread-14 localuser :a.User@13c5982
current thread name :Thread-16 localuser :a.User@13c5982
current thread name :Thread-10 localuser :a.User@13c5982
current thread name :Thread-15 localuser :a.User@13c5982
current thread name :Thread-12 localuser :a.User@13c5982
current thread name :Thread-18 localuser :a.User@13c5982
current thread name :Thread-8 localuser :a.User@13c5982
此時確實得到了期望的結果。