ThreadLocal原理
一、定義
當(dāng)需要為每一個(gè)線程設(shè)置一個(gè)私有的變量,進(jìn)行線程隔離時(shí),java提供的ThreadLocal可以幫助我們實(shí)現(xiàn),ThreadLocal有一個(gè)內(nèi)部ThreadLocalMap,存儲(chǔ)每個(gè)ThreadLocal對(duì)象和它的值,value為該線程獨(dú)有的數(shù)據(jù),可以為多種類型
ThreadLocal、ThreadLocalMap、Thread三者之間的關(guān)系
1、ThreadLocal是一個(gè)本地線程副本變量工具類,主要用于將私有線程和該線程存放的副本對(duì)象做一個(gè)映射,各個(gè)線程之間的變量互不干擾,線程之間數(shù)據(jù)隔離,可通過set、get方法操作當(dāng)前線程副本數(shù)據(jù)
2、ThreadLocalMap類是ThreadLocal的靜態(tài)內(nèi)部類,通過操作Entry來存儲(chǔ)Thread和它對(duì)應(yīng)的數(shù)據(jù)。
3、Thread類比較常用,線程類內(nèi)部維持一個(gè)ThreadLocalMap類實(shí)例(threadLocals)
ThreadLocalMap是Thread的成員變量,又是ThreadLocal的靜態(tài)內(nèi)部類,線程數(shù)據(jù)通過ThreadLocal來操控
二、使用方式
直接創(chuàng)建ThreadLocal對(duì)象,定義保存的數(shù)據(jù)類型,通過set方法設(shè)置值,get方法來取出
ThreadLocal<String> t=new ThreadLocal<>(); t.set("hello"); System.out.println(t.get());注意:
1.這里只能set一次,再次在該線程set時(shí)會(huì)失效,只保留最新的值
2.ThreadLocal類中的ThreadLocalMap實(shí)體Entry繼承自WeakReference,是虛引用對(duì)象,只要發(fā)生垃圾回收GC,就會(huì)被回收掉ThreadLocalMap保存的value值
三、源碼解析
1.set方法
實(shí)際是獲取到當(dāng)前的線程Thread和ThreadLocalMap對(duì)象,沒有則創(chuàng)建該map
public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value); }map.set方法,獲取當(dāng)前對(duì)象的的索引,通過線性探測(cè)法來放入對(duì)象,有沖突,往Entry數(shù)組下一個(gè)坐標(biāo)位置移動(dòng)
private void set(ThreadLocal<?> key, Object value) {// We don't use a fast path as with get() because it is at// least as common to use set() to create new entries as// it is to replace existing ones, in which case, a fast// path would fail more often than not.Entry[] tab = table;int len = tab.length;int i = key.threadLocalHashCode & (len-1);for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {ThreadLocal<?> k = e.get();if (k == key) {e.value = value;return;}if (k == null) {replaceStaleEntry(key, value, i);return;}}tab[i] = new Entry(key, value);int sz = ++size;if (!cleanSomeSlots(i, sz) && sz >= threshold)rehash(); }2.createMap方法
創(chuàng)建ThreadLocalMap,并賦值給當(dāng)前的thread的成員變量
1)創(chuàng)建了一個(gè)Entry數(shù)組,大小為16,且該數(shù)組是繼承自WeakReference,表明在gc時(shí)會(huì)被回收掉
?table = new Entry[INITIAL_CAPACITY];static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;} }2)計(jì)算散列索引位置并放入Entry數(shù)組
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);table[i] = new Entry(firstKey, firstValue);private final int threadLocalHashCode = nextHashCode();private static final int HASH_INCREMENT = 0x61c88647;private static int nextHashCode() {return nextHashCode.getAndAdd(HASH_INCREMENT);}與HashMap一樣,進(jìn)行散列時(shí),通過hashCode值和&運(yùn)算來計(jì)算當(dāng)前線程threadLocal的在threadLocalMap中的位置,每個(gè)ThreadLocal對(duì)象都共享同一個(gè)nextHashCode,每個(gè)ThreadLocal的HashCode值都會(huì)在當(dāng)前值上加上一個(gè)16進(jìn)制數(shù)0x61c88647,保證**實(shí)現(xiàn)了不同的ThreadLocal對(duì)象的threadLocalHashCode基本不會(huì)相同**
&屬于位運(yùn)算,為了將該threadLocal盡量充分均勻的散列到Entry數(shù)組中,且計(jì)算更高效
解決Entry數(shù)組沖突的方法時(shí)開放定址法的線性探測(cè)法,每次加上一個(gè)數(shù),再取余,有沖突
3)設(shè)置擴(kuò)容因子
setThreshold(INITIAL_CAPACITY);private void setThreshold(int len) {threshold = len * 2 / 3; }3.get方法
獲取ThreadLocalMap中當(dāng)前線程對(duì)象對(duì)應(yīng)的value值
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為null,則創(chuàng)建map,并設(shè)置當(dāng)前線程key在ThreadLocalMap的value為null
private T setInitialValue() {T value = initialValue();Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);return value; }4.remove方法
從ThreadLocalMap中移除當(dāng)前線程對(duì)象和它持有value
public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());if (m != null)m.remove(this); }5.createInheritedMap方法
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {return new ThreadLocalMap(parentMap); }該方法是在Thread中調(diào)用的,Thread類中維護(hù)了一個(gè)ThreadLocalMap成員變量,用于初始化該mapi
f (parent.inheritableThreadLocals != null)this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);主要是通過當(dāng)前線程thread獲取ThreadLocalMap對(duì)象,初始化數(shù)組和負(fù)載因子threshold,遍歷節(jié)點(diǎn)加入數(shù)組。
private ThreadLocalMap(ThreadLocalMap parentMap) {Entry[] parentTable = parentMap.table;int len = parentTable.length;setThreshold(len);table = new Entry[len];for (int j = 0; j < len; j++) {Entry e = parentTable[j];if (e != null) {@SuppressWarnings("unchecked")ThreadLocal<Object> key = (ThreadLocal<Object>) 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++;}}} }?
?
?
?
?
?
?
?
?
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的ThreadLocal原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java和Spring中线程池创建方法
- 下一篇: Spring中策略模式实现方法