- 其實很簡單,就是創建一個對象,然后每個線程去訪問時,訪問的是這個對象的副本。即該對象會為每個線程拷貝出一個副本。
- 其實效果和local variable是一個效果。即在線程內初始化一個本地變量。
ThreadLocal<String> threadLocalOld = new ThreadLocal<String>(){
@Override
protected String initialValue() {
return new String("dsadsa");
}
};
Thread thread = new Thread(()->{
String s = threadLocalOld.get();//每個線程調度get函數獲取本線程的副本。
// do sth
threadLocalOld.set("dsad");// set函數set的值,只會設置本線程的值,不會對其他線程有任何影響。
});
Thread thread1 = new Thread(()->{
String s = new String("dsadsa");//ThreadLocal效果定義local variable類似。
// do sth
});
但ThreadLocal要比local variable優點:
- 當我們需要給線程傳遞數據時,
如果這個數據是線程安全的(不可變的)或線程處理中是只讀操作,那線程中直接使用即可。
如果這個數據是非線程安全的,則使用ThreadLocal是一個好的選擇。相比在線程中定義一個這個數據的副本要好。因為ThreadLocal有更大的scope。 - 線程內全局變量的效果。
我們在線程內可以通過set和get得到,即set的值可以在線程內anywhere通過get得到 。所以如果線程函數內有多層嵌套調用,則無需傳遞此值。其實相當于線程內的全局變量。
ThreadLocal的誤用:
public static void main(String[] args){
TT tt = new TT("a", 1);
ThreadLocal<TT> ttThreadLocal = ThreadLocal.withInitial(()->tt);
Thread t1 = new Thread(()->{
ttThreadLocal.get().setS("b");
});
t1.start();
try {
t1.join();
System.out.println(JSON.toJSONString(tt));
} catch (Exception e) {
e.printStackTrace();
}
}
此時的輸出是{"i":1,"s":"b"}。
即initialValue的誤用。即ThreadLocal在線程調用的時候就會去調用initiaValue來得到本線程的副本,而如果用戶自己寫的initialValue函數并不是new一個對象,而是直接返回全局變量,則這里就會出錯。即t1線程修改的值在主線程中可以看到。