ThreadLocal基本解析
點擊上方藍色字體,選擇“標星公眾號”
優(yōu)質(zhì)文章,第一時間送達
一 、基本了解
1.1 作用
1.2 基本方法
1.3 入門案例
package ThreadLocal;
/**
* @author shu
* @date 2021/6/6
* @description
*/
public class ThreadLocalText {
//變量
private String variable;
public String getVariable() {
return variable;
}
public void setVariable(String variable) {
this.variable = variable;
}
public static void main(String[] args) {
ThreadLocalText text = new ThreadLocalText();
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
synchronized (ThreadLocalText.class) {
text.setVariable(Thread.currentThread().getName() + "變量");
System.out.println("<------------------------------------->");
String variable = text.getVariable();
System.out.println(Thread.currentThread().getName() + "--->" + variable);
}
}
}).start();
}
}
}
package ThreadLocal;
/**
* @author shu
* @date 2021/6/6
* @description
*/
public class ThreadLocalText {
private static ThreadLocal<String> t1 = new ThreadLocal<>();
//變量
private String variable;
public String getVariable() {
return t1.get();
}
public void setVariable(String variable) {
t1.set(variable);
}
public static void main(String[] args) {
ThreadLocalText text = new ThreadLocalText();
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
//synchronized (ThreadLocalText.class) {
text.setVariable(Thread.currentThread().getName() + "變量");
String variable = text.getVariable();
System.out.println(Thread.currentThread().getName() + "--->" + variable);
}
// }
}).start();
}
}
}
二、 原理探究
2.1 內(nèi)部結(jié)構(gòu)
2.2 ThreadLocalMap源碼分析
/**
* 初始容量 —— 必須是2的整次冪
*/
private static final int INITIAL_CAPACITY = 16;
/**
* 存放數(shù)據(jù)的table,Entry類的定義在下面分析
* 同樣,數(shù)組長度必須是2的整次冪。
*/
private Entry[] table;
/**
* 數(shù)組里面entrys的個數(shù),可以用于判斷table當前使用量是否超過閾值。
*/
private int size = 0;
/**
* 進行擴容的閾值,表使用量大于它的時候進行擴容。
*/
private int threshold; // Default to 0
//規(guī)定死是弱引用 WeakReference
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//初始化table
table = new Entry[INITIAL_CAPACITY];
//計算插入值的地址
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//設(shè)置值
table[i] = new Entry(firstKey, firstValue);
size = 1;
//設(shè)置閾值
setThreshold(INITIAL_CAPACITY);
}
//計算索引值 threadLocalHashCode
private final int threadLocalHashCode = nextHashCode();
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
//AtomicInteger是一個提供原子操作的Integer類,通過線程安全的方式操作加減,適合高并發(fā)情況下的使用
private static AtomicInteger nextHashCode = new AtomicInteger();
//特殊的hash值
private static final int HASH_INCREMENT = 0x61c88647;
// INITIAL_CAPACITY - 1
private void set(ThreadLocal<?> key, Object value) {
ThreadLocal.ThreadLocalMap.Entry[] tab = table;
int len = tab.length;
//計算索引(重點代碼,剛才分析過了)
int i = key.threadLocalHashCode & (len-1);
/**
* 使用線性探測法查找元素(重點代碼)
*/
for (ThreadLocal.ThreadLocalMap.Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
//ThreadLocal 對應(yīng)的 key 存在,直接覆蓋之前的值
if (k == key) {
e.value = value;
return;
}
// key為 null,但是值不為 null,說明之前的 ThreadLocal 對象已經(jīng)被回收了,
// 當前數(shù)組中的 Entry 是一個陳舊(stale)的元素
if (k == null) {
//用新元素替換陳舊的元素,這個方法進行了不少的垃圾清理動作,防止內(nèi)存泄漏
replaceStaleEntry(key, value, i);
return;
}
}
//ThreadLocal對應(yīng)的key不存在并且沒有找到陳舊的元素,則在空元素的位置創(chuàng)建一個新的Entry。
tab[i] = new Entry(key, value);
int sz = ++size;
/**
* cleanSomeSlots用于清除那些e.get()==null的元素,
* 這種數(shù)據(jù)key關(guān)聯(lián)的對象已經(jīng)被回收,所以這個Entry(table[index])可以被置null。
* 如果沒有清除任何entry,并且當前使用量達到了負載因子所定義(長度的2/3),那么進行 * rehash(執(zhí)行一次全表的掃描清理工作)
*/
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
/**
* 獲取環(huán)形數(shù)組的下一個索引
*/
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
2.3基本方法詳解
/**
* 設(shè)置當前線程對應(yīng)的ThreadLocal的值
*
* @param value 將要保存在當前線程對應(yīng)的ThreadLocal的值
*/
public void set(T value) {
// 獲取當前線程對象
Thread t = Thread.currentThread();
// 獲取此線程對象中維護的ThreadLocalMap對象
ThreadLocalMap map = getMap(t);
// 判斷map是否存在
if (map != null)
// 存在則調(diào)用map.set設(shè)置此實體entry
map.set(this, value);
else
// 1)當前線程Thread 不存在ThreadLocalMap對象
// 2)則調(diào)用createMap進行ThreadLocalMap對象的初始化
// 3)并將 t(當前線程)和value(t對應(yīng)的值)作為第一個entry存放至ThreadLocalMap中
createMap(t, value);
}
/**
* 獲取當前線程Thread對應(yīng)維護的ThreadLocalMap
*
* @param t the current thread 當前線程
* @return the map 對應(yīng)維護的ThreadLocalMap
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
/**
*創(chuàng)建當前線程Thread對應(yīng)維護的ThreadLocalMap
*
* @param t 當前線程
* @param firstValue 存放到map中第一個entry的值
*/
void createMap(Thread t, T firstValue) {
//這里的this是調(diào)用此方法的threadLocal
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
/**
* 返回當前線程中保存ThreadLocal的值
* 如果當前線程沒有此ThreadLocal變量,
* 則它會通過調(diào)用{@link #initialValue} 方法進行初始化值
*
* @return 返回當前線程對應(yīng)此ThreadLocal的值
*/
public T get() {
// 獲取當前線程對象
Thread t = Thread.currentThread();
// 獲取此線程對象中維護的ThreadLocalMap對象
ThreadLocalMap map = getMap(t);
// 如果此map存在
if (map != null) {
// 以當前的ThreadLocal 為 key,調(diào)用getEntry獲取對應(yīng)的存儲實體e
ThreadLocalMap.Entry e = map.getEntry(this);
// 對e進行判空
if (e != null) {
@SuppressWarnings("unchecked")
// 獲取存儲實體 e 對應(yīng)的 value值
// 即為我們想要的當前線程對應(yīng)此ThreadLocal的值
T result = (T)e.value;
return result;
}
}
/*
初始化 : 有兩種情況有執(zhí)行當前代碼
第一種情況: map不存在,表示此線程沒有維護的ThreadLocalMap對象
第二種情況: map存在, 但是沒有與當前ThreadLocal關(guān)聯(lián)的entry
*/
return setInitialValue();
}
/**
* 初始化
*
* @return the initial value 初始化后的值
*/
private T setInitialValue() {
// 調(diào)用initialValue獲取初始化的值
// 此方法可以被子類重寫, 如果不重寫默認返回null
T value = initialValue();
// 獲取當前線程對象
Thread t = Thread.currentThread();
// 獲取此線程對象中維護的ThreadLocalMap對象
ThreadLocalMap map = getMap(t);
// 判斷map是否存在
if (map != null)
// 存在則調(diào)用map.set設(shè)置此實體entry
map.set(this, value);
else
// 1)當前線程Thread 不存在ThreadLocalMap對象
// 2)則調(diào)用createMap進行ThreadLocalMap對象的初始化
// 3)并將 t(當前線程)和value(t對應(yīng)的值)作為第一個entry存放至ThreadLocalMap中
createMap(t, value);
// 返回設(shè)置的值value
return value;
}
/**
* 刪除當前線程中保存的ThreadLocal對應(yīng)的實體entry
*/
public void remove() {
// 獲取當前線程對象中維護的ThreadLocalMap對象
ThreadLocalMap m = getMap(Thread.currentThread());
// 如果此map存在
if (m != null)
// 存在則調(diào)用map.remove
// 以當前ThreadLocal為key刪除對應(yīng)的實體entry
m.remove(this);
}
/**
* 返回當前線程對應(yīng)的ThreadLocal的初始值
* 此方法的第一次調(diào)用發(fā)生在,當線程通過get方法訪問此線程的ThreadLocal值時
* 除非線程先調(diào)用了set方法,在這種情況下,initialValue 才不會被這個線程調(diào)用。
* 通常情況下,每個線程最多調(diào)用一次這個方法。
*
* <p>這個方法僅僅簡單的返回null {@code null};
* 如果程序員想ThreadLocal線程局部變量有一個除null以外的初始值,
* 必須通過子類繼承{@code ThreadLocal} 的方式去重寫此方法
* 通常, 可以通過匿名內(nèi)部類的方式實現(xiàn)
*
* @return 當前ThreadLocal的初始值
*/
protected T initialValue() {
return null;
}
三 、面試
3.1 為什么ThreadLocalMap的key要設(shè)計成弱引用?


評論
圖片
表情





