摘要:返回索引位置的值。因為依賴于靜態成員變量的關系,所以它的肯定唯一獲取當前線程。位置還沒有初始化第一次這個,直接將放到的位置。在線程池模式下,生命周期伴隨著線程一直存在,可能出現內存泄漏的情況,最好手動調用方法。
前言本文原創地址,:jsbintask的博客(食用效果最佳),轉載請注明出處!
ThreadLocal是jdk中一個非常重要的工具,它可以控制堆內存中的對象只能被指定線程訪問,如果你經常閱讀源碼,基本在各大框架都能發現它的蹤影。而它最經典的應用就是事務管理,同時它也是面試中的常客。
原理我們知道,堆內存是共享的,為什么ThreadLocal能夠控制指定線程訪問呢? 如圖:
調用ThreadLocal的get方法。
獲取當前線程t1.
獲取t1的成員變量ThreadLocalMap。
根據ThreadLocal的hashcode計算出ThreadLocalMap中Entry[]數組的索引。
返回索引位置的值。 這樣我們就很容易理解了,為什么只有當前線程才能獲取到某些值,因為這是這些值都直接保存在當前線程的成員變量ThreadLocalMap中,而ThreadLocal在這個過程中充當的角色則是提供它獨一無二的hashcode值,這樣我們就能計算出我們保存的值在ThreadLocalMap的位置。
我們從構建一個ThreadLocal到調用它的set,get方法完整的分析一遍它的源碼。
構造器當我們使用new ThreadLocal<>() new一個ThreadLocal對象時,它初始化了一個成員變量threadLocalHashCode,這個成員變量代表當前ThreadLocal的hashcode值,而它肯定是唯一的:
ThreadLocal內部有一個靜態hashCode生成器nextHashCode。
每次新new一個ThreadLocal對象,調用這個生成器同步方法獲取hashcode。 因為依賴于靜態成員變量nextHashCode的關系,所以它的hashcode肯定唯一!
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
獲取當前線程t。
從t中獲取ThreadLocalMap map。
如果map不為空,將當前值value放入map。
如果map為空,新建一個ThreadLocalMap放入線程t。 ThreadLocalMap是ThreadLocal中的內部類,它的結構如下:
public class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<");{
Object value;
Entry(ThreadLocal<");super(k);
value = v;
}
}
private Entry[] table;
private int size = 0;
private static final int INITIAL_CAPACITY = 16;
private int threshold; // Default to 0
}
類似于ArrayList內部的構造,它內部有一個Entry數組table,并且Entry繼承自弱引用(gc時保存的ThreadLocal會被標記清理),所以每一個Entry中保存著兩個值,ThreadLocal,value,value既是我們要保存的值。 接著,我們回過頭詳細分析第三步,ThreadLocalMap的set方法:
private void set(ThreadLocal<"); {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1); // 1
for (Entry e = tab[i];
e != null; // 2
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<");if (k == key) { // 3
e.value = value;
return;
}
if (k == null) { // 4
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value); // 5
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold) // 6
rehash();
}
根據ThreadLocal的hashCode計算出在entry中的索引i。
取出i對應的Entry值e。
如果e的key等于當前ThreadLocal,代表已經有一個一樣的ThreadLocal在這個entry設值,直接替換這個entry上的value。
e上面的ThreadLocal為null,代表垃圾收集器準備回收這個Entry了,重新計算數組大小,重新hash。
i位置還沒有初始化(第一次set這個ThreadLocal),直接將value放到i的位置。
擴容Entry數組。
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();
}
獲取當前線程。
從當前線程中獲取ThreadLocalMap
從ThreadLocalMap中找出ThreadLocal對應的Entry.
如果Entry不為null,直接返回Entry中的value
返回初始值。 其中,ThreadLocalMap的get(ThreadLocal tl)如下: 它和我們一開始的分析一樣,根據ThreadLocal的hashcode成員變量計算出索引位置i,得到Entry。這里同樣有特殊情況,如果得到的Entry的key和當前ThreadLocal不相等,代表這個Entry將被垃圾收集處理,調用getEntryAfterMiss rehash,計算數組大小。
從上面的代碼分析中,我們知道,ThreadLocalMap的生命周期和當前線程同步,如果當前線程被銷毀,則map中的所有引用均被銷毀。但如果當前線程不被銷毀呢(線程池,tomcat處理請求等)?Entry中保存了ThreadLocal的弱引用以及value,gc時可能清理掉ThreadLocal,而這個value確再沒有訪問之地,這個時候就會造成內存泄漏! 所以我們需要手動調用remove方法清理掉當前線程ThreadLocalMap的引用!
總結ThreadLocal中真正保存的值還是在線程的ThreadLocalMap中,ThreadLocal只是使用它的hashcode值充當中間計算變量。
ThreadLocalMap內部使用一個Entry數組保存數據,它根據ThreadLocal計算出來的hashcode得到Entry的索引值,而ThreadLocal的hashcod在其內部的靜態工具類產生,所以不會出現沖突。
在線程池模式下,ThreadLocal生命周期伴隨著線程一直存在,可能出現內存泄漏的情況,最好手動調用remove方法。
關注我,這里只有干貨!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/7868.html
摘要:多線程類庫對于共享數據的讀寫控制主要采用鎖機制保證線程安全,本文所要探究的則采用了一種完全不同的策略。所以出現內存泄露的前提必須是持有的線程一直存活,這在使用線程池時是很正常的,在這種情況下一直不會被,因為 Java 多線程類庫對于共享數據的讀寫控制主要采用鎖機制保證線程安全,本文所要探究的 ThreadLocal 則采用了一種完全不同的策略。ThreadLocal 不是用來解決共享數...
摘要:提供了線程安全的共享對象,在編寫多線程代碼時,可把不安全的整個變量封裝進,或者把該對象與線程相關的狀態使用保存并不能替代同步機制,兩者面向的問題領域不同。 ThreadLocal類 使用ThreadLocal類可以簡化多線程編程時的并發訪問,使用這個工具類可以很簡捷地隔離多線程程序的競爭資源。Java5之后,為ThreadLocal類增加了泛型支持,即ThreadLocal Threa...
摘要:并沒有提供語言級的線程局部變量,而是在類庫里提供了線程局部變量的功能,也就是這次的主角類。 Yuicon 轉載請注明原創出處,謝謝! 序 在多線程環境下,訪問非線程安全的變量時必須進行線程同步,例如使用synchronized方式訪問HashMap實例。但是同步訪問會降低并發性,影響系統性能。這時候就可以用空間換時間,如果我們給每個線程都分配一個獨立的變量,就可以用非同步的方式使用非...
摘要:進程一般由程序數據集進程控制塊三部分組成。線程概述線程的出現是為了降低上下文切換的消耗,提高系統的并發性。線程突破了一個進程只能干一件事的缺陷,使到進程內并發成為可能。進程與線程的關系進程是計算機中的程序關于某數據集合上的一次運行活動。 進程概述 進程:正在運行的程序,是系統進行資源分配和調用的獨立單位。 進程就是一個程序在一個數據集上的一次動態執行過程。 進程一般由程序、數據集、進...
閱讀 901·2021-09-22 15:17
閱讀 1924·2021-09-22 15:06
閱讀 2222·2021-09-08 09:35
閱讀 5109·2021-09-01 11:43
閱讀 3483·2019-08-30 15:55
閱讀 2156·2019-08-30 12:48
閱讀 3157·2019-08-30 12:45
閱讀 1787·2019-08-29 17:31