Java基礎解析系列(七)---ThreadLocal原理分析
目錄
- java基礎解析系列(一)---String、StringBuffer、StringBuilder
- java基礎解析系列(二)---Integer緩存及裝箱拆箱
- java基礎解析系列(三)---HashMap原理
- java基礎解析系列(四)---LinkedHashMap的原理及LRU算法的實現
- java基礎解析系列(五)---HashMap並發下的問題以及HashTable和CurrentHashMap的區別
- java基礎解析系列(六)---註解原理及使用
- 這是我的博客目錄,歡迎閱讀
作用
- 與同步機制區分開來,同步機制是為了解決在共享情況下並發導致的問題。而ThreadLocal是避免了共享
- 在多線程情況下,為了避免共享,我們可以采用多線程多實例的方式,也可以使用ThreadLocal來避免共享沖突
什麽是ThreadLocal
- ThreadLocal提供了線程本地變量,它可以保證訪問到的變量屬於當前線程,每個線程都保存有一個變量副本,每個線程的變量都不同。ThreadLocal相當於提供了一種線程隔離,將變量與線程相綁定
實驗
public class T {
ThreadLocal<Long> longLocal = new ThreadLocal<Long>();
public void set() {
longLocal.set(Thread.currentThread().getId());
}
public long getLong() {
return longLocal.get();
}
public static void main(String[] args) throws InterruptedException {
final T test = new T();
test.set();
Thread thread1 = new Thread(){
public void run() {
test.set();
system.out.println("線程一:"+test.getLong());
};
};
thread1.start();
thread1.join();
System.out.println("main線程:"+test.getLong());
System.out.println("沒有發生值的覆蓋,兩個線程保存的值是不同的");
}
}
- 輸出: 線程一:11 main線程:1 沒有發生值的覆蓋,兩個線程保存的值是不同的
- 證明ThreadLocal確實為變量在每個線程中都創建了一個副本
Thread的成員
ThreadLocal.ThreadLocalMap threadLocals = null;
- Thread有一個ThreadLocalMap成員
ThreadLocal的set方法
179 public void set(T value) {
180 Thread t = Thread.currentThread();
181 ThreadLocalMap map = getMap(t);
182 if (map != null)
183 map.set(this, value);
184 else
185 createMap(t, value);
186 }
- 181行通過當前線程獲取ThreadLocalMap
212 ThreadLocalMap getMap(Thread t) {
213 return t.threadLocals;
214 }
- 185行如果當前線程的成員threadLocals還是空的,創建一個map
224 void createMap(Thread t, T firstValue) {
225 t.threadLocals = new ThreadLocalMap(this, firstValue);
226 }
- 將當前的ThreadLocal對象和value作為參數
328 ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
329 table = new Entry[INITIAL_CAPACITY];
330 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
331 table[i] = new Entry(firstKey, firstValue);
332 size = 1;
333 setThreshold(INITIAL_CAPACITY);
334 }
- 通過ThreadLocalMap的構造方法可以看到,該方法創建一個Entry數組,然後通過傳入的key(當前ThreadLocal對象)計算在數組中的下標,然後將Entry放入數組。
- Thread的ThreadLocalMap存放的Entry,鍵是不同的ThreadLoacal對象,也就是說一個線程綁定多個ThreadLocal對象
- 那麽也就是說,ThreadLocal設置值的時候,這個值是存放在當前線程的一個map(這個map存放了多個ThreadLocal對象)裏面,因此,不同線程之間即避免了共享
ThreadLocalMap的set方法
416 private void set(ThreadLocal key, Object value) {
417
418 // We don't use a fast path as with get() because it is at
419 // least as common to use set() to create new entries as
420 // it is to replace existing ones, in which case, a fast
421 // path would fail more often than not.
422
423 Entry[] tab = table;
424 int len = tab.length;
425 int i = key.threadLocalHashCode & (len-1);
426
427 for (Entry e = tab[i];
428 e != null;
429 e = tab[i = nextIndex(i, len)]) {
430 ThreadLocal k = e.get();
431
432 if (k == key) {
433 e.value = http://www.cnblogs.com/-new/p/value;
434 return;
435 }
436
437 if (k == null) {
438 replaceStaleEntry(key, value, i);
439 return;
440 }
441 }
442
443 tab[i] = new Entry(key, value);
444 int sz = ++size;
445 if (!cleanSomeSlots(i, sz) && sz >= threshold)
446 rehash();
447 }
- 分析這段代碼,他解決hash沖突的辦法不同與hashmap使用鏈表來解決沖突問題。通過計算key的hashcode獲取數組中的下標後,然後進入427行,432判斷要放入的鍵是否和該下標中原來的鍵相同,是的話進行值的覆蓋。如果為空的,放入該Entry。如果不同的話且不為空,看當前下標+1的位置,同樣進入循環。依次執行下去。
ThreadLocal的get方法
142 public T get() {
143 Thread t = Thread.currentThread();
144 ThreadLocalMap map = getMap(t);
145 if (map != null) {
146 ThreadLocalMap.Entry e = map.getEntry(this);
147 if (e != null)
148 return (T)e.value;
149 }
150 return setInitialValue();
151 }
- 可以發現,執行get方法的時候,也是先獲取當前線程,然後獲得該線程的一個ThreadLocalMap成員,通過這個map和和當前的ThreadLocal對象作為鍵,來獲取value
內存泄露
271 static class Entry extends WeakReference<ThreadLocal> {
- 如果ThreadLocal不使用弱引用(這篇文章有介紹),那麽當ThradLocal th=null時候,因為ThreadLocalMap仍然有th的強引用,所以並不能回收。而如果key使用弱引用的時候,th為null的時候,下次回收的時候就會將這個key回收
- 但是有一個問題,雖然這個key可以被回收,但是這個value仍然有強引用,並不能回收。如果當前線程不結束,並且不調用set/get/remove方法(這些方法會對key為null的entry進行釋放),這片內存會被一直占用。這就是內存泄露的原因
- 因此在用完ThreadLocal的時候,記得執行remove方法,避免內存泄露
簡單總結
- 同一個ThreadLocal對象,不同的線程,不同的ThreadLocal對象的值
我覺得分享是一種精神,分享是我的樂趣所在,不是說我覺得我講得一定是對的,我講得可能很多是不對的,但是我希望我講的東西是我人生的體驗和思考,是給很多人反思,也許給你一秒鐘、半秒鐘,哪怕說一句話有點道理,引發自己內心的感觸,這就是我最大的價值。(這是我喜歡的一句話,也是我寫博客的初衷)
作者:jiajun 出處: http://www.cnblogs.com/-new/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。如果覺得還有幫助的話,可以點一下右下角的【推薦】,希望能夠持續的為大家帶來好的技術文章!想跟我一起進步麽?那就【關註】我吧。
Tags: ThreadLocal 線程 解析 基礎 系列 變量
文章來源: