ThreadLocal簡(jiǎn)介
Java中的ThreadLocal類給每個(gè)線程分配一個(gè)只屬于該線程的變量副本欠窒,可以用來實(shí)現(xiàn)線程間的數(shù)據(jù)隔離悦污,當(dāng)前線程的變量不能被其他線程訪問铸屉。
ThreadLocal使用
創(chuàng)建ThreadLocal變量
private ThreadLocal myThreadLocal = new ThreadLocal();
訪問ThreadLocal變量
設(shè)置需要保存的值:
myThreadLocal.set("ThreadLocal value");
讀取保存在ThreadLocal變量中的值:
String threadLocalVlaue = (String) myThreadLocal.get();
ThreadLocal范型
private ThreadLocal myThreadLocal = new ThreadLocal<String>()
初始化ThreadLocal的值
private ThreadLocal myThreadLocal = new ThreadLocal<String>(){
protected String initialVlaue(){
return "initial value";
}
};
源碼分析
源碼版本:
jdk7u80
最常用的方法就是get和set方法,所以先從這兩個(gè)方法入手切端,分析下使用彻坛。
set(T value)
將當(dāng)前的線程局部變量的副本的值設(shè)置為指定的值。子類一般不需要重寫該方法踏枣,只需要使用initialValue方法去設(shè)置初始值昌屉。
public void set(T value) {
//獲取當(dāng)前線程
Thread t = Thread.currentThread();
//從當(dāng)前線程中得到當(dāng)前線程的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
//不為空的話,調(diào)用ThreadLocalMap的set方法設(shè)置值
map.set(this, value);
else
//ThreadLocalMap為null茵瀑,還沒有被初始化间驮,創(chuàng)建新的map
createMap(t, value);
}
getMap(Thread t)
獲取指定的線程t的ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
createMap(t, value)
為當(dāng)前線程t初始化一個(gè)ThreadLocalMap用來存儲(chǔ)值,初始值是value马昨。
在Thread類中ThreadLocal.ThreadLocalMap threadLocals = null;
是用來存儲(chǔ)當(dāng)前線程對(duì)應(yīng)的ThreadLocalMap竞帽,屬于線程私有的。所以createMap方法使用t.threadLocals = new ThreadLocalMap(this, firstValue);
來設(shè)置鸿捧。
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocal用來把變量的副本存儲(chǔ)到線程中屹篓,變量的副本就只能是當(dāng)前線程私有,而在線程中是通過ThreadLocalMap來存儲(chǔ)副本的匙奴,所以有必要了解下ThreadLocalMap是怎么實(shí)現(xiàn)的堆巧。
ThreadLocalMap
ThreadLocalMap是一個(gè)自定義的HashMap,用來存儲(chǔ)線程本地變量的值泼菌,類似與Map谍肤。ThreadLocalMap內(nèi)部是使用Entry對(duì)象來存儲(chǔ)。
Entry
Entry繼承了WeakReference哗伯,使用ThreadLocal作為key谣沸,value為ThreadLocal的值。
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
private static final int INITIAL_CAPACITY = 16;
ThreadLocalMap的初始容量為16笋颤。
private Entry[] table;
存放線程本地變量的數(shù)組。
private int size = 0;
線程本地變量的數(shù)目。
private int threshold
擴(kuò)容的閾值伴澄。
擴(kuò)容的閾值為指定長(zhǎng)度的三分之二
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
//構(gòu)造方法赋除,當(dāng)我們第一次使用的時(shí)候會(huì)構(gòu)造一個(gè)新的ThreadLocalMap
ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
//存放線程本地變量的數(shù)組,初始容量16
table = new Entry[INITIAL_CAPACITY];
//得到存放Entry的數(shù)組下標(biāo)
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//在得出的位置處新建一個(gè)Entry對(duì)象
table[i] = new Entry(firstKey, firstValue);
//大小設(shè)為1
size = 1;
//設(shè)置閾值 16*2/3
setThreshold(INITIAL_CAPACITY);
}
使用給定的父map來構(gòu)造一個(gè)ThreadLocalMap
private ThreadLocalMap(ThreadLocalMap parentMap) {
//父map中存放的線程本地變量數(shù)據(jù)
Entry[] parentTable = parentMap.table;
//父map的長(zhǎng)度
int len = parentTable.length;
//設(shè)置閾值
setThreshold(len);
//新建長(zhǎng)度為len的Entry數(shù)組
table = new Entry[len];
//循環(huán)把父map的數(shù)組的元素放到新數(shù)組中去非凌,中間需要重新計(jì)算數(shù)組下標(biāo)举农。
for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
ThreadLocal key = e.get();
if (key != null) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
根據(jù)key獲取Entry
private Entry getEntry(ThreadLocal key) {
//計(jì)算數(shù)組下標(biāo)
int i = key.threadLocalHashCode & (table.length - 1);
//獲取元素
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
//沒有找到key的時(shí)候的處理
return getEntryAfterMiss(key, i, e);
}
//當(dāng)沒有找到對(duì)應(yīng)的key時(shí)候
//key ThreadLocal對(duì)象
//i 計(jì)算出來的數(shù)組下標(biāo)
//e 在i處的entry
private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
//獲取key
ThreadLocal k = e.get();
//找到key,返回e
if (k == key)
return e;
//key為null敞嗡,找不到颁糟,清除掉
if (k == null)
expungeStaleEntry(i);
else
//key不為null,計(jì)算下一個(gè)數(shù)組下標(biāo)
i = nextIndex(i, len);
//返回下一個(gè)entry
e = tab[i];
}
return null;
}
//存放指定的key和value
private void set(ThreadLocal key, Object value) {
//當(dāng)前存放的數(shù)組
Entry[] tab = table;
//數(shù)組長(zhǎng)度
int len = tab.length;
//根據(jù)key獲取存放的數(shù)組下標(biāo)
int i = key.threadLocalHashCode & (len-1);
//從第i個(gè)元素開始挨個(gè)遍歷
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
//獲取到i處的key
ThreadLocal k = e.get();
//i處的key和要存放的key相等喉悴,將原來的值替換成新的值棱貌,返回。
if (k == key) {
e.value = value;
return;
}
//i處key為null
if (k == null) {
//替換原來的Entry
replaceStaleEntry(key, value, i);
return;
}
}
//不存在key箕肃,新建一個(gè)Entry
tab[i] = new Entry(key, value);
//size加1
int sz = ++size;
//不能移除一些舊的entry并且新的size已經(jīng)大于等于閾值了婚脱,需要重新擴(kuò)容
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
//rehash()
private void rehash() {
//首先清除舊的entry
expungeStaleEntries();
// size大于等于閾值的四分之三,將容量擴(kuò)展為兩倍
if (size >= threshold - threshold / 4)
resize();
}
ThreadLocalMap內(nèi)部的Entry的get和set基本就這些勺像,接下來繼續(xù)看ThreadLocal的get方法
public T get()
public T get() {
//獲取當(dāng)前線程
Thread t = Thread.currentThread();
//獲取當(dāng)前線程的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
//map為空障贸,設(shè)置初始值,并返回
return setInitialValue();
}
private T setInitialValue()
private T setInitialValue() {
//這里初始值為null
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
remove()
移除當(dāng)前線程的ThreadLocalMap中的值
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
總結(jié)一下get和set方法
get方法:
首先獲取到當(dāng)前線程吟宦,然后獲取當(dāng)前線程內(nèi)部的ThreadLocalMap篮洁,如果map不為空,就查找到Entry中的值殃姓,返回袁波;如果map為空,設(shè)置初始值辰狡,并返回锋叨。
set方法:
首先獲取到當(dāng)前線程,然后獲取當(dāng)前線程內(nèi)部的ThreadLocalMap宛篇,如果map不為空娃磺,直接使用Entry的set設(shè)置值,此方法會(huì)替換原來的值叫倍;如果map為空偷卧,說明沒有使用過,新建一個(gè)map并使用當(dāng)前線程和指定的值初始化吆倦。