ThreadLocal原理
一、定義
當需要為每一個線程設置一個私有的變量,進行線程隔離時,java提供的ThreadLocal可以幫助我們實現,ThreadLocal有一個內部ThreadLocalMap,存儲每個ThreadLocal對象和它的值,value為該線程獨有的數據,可以為多種類型
ThreadLocal、ThreadLocalMap、Thread三者之間的關系
1、ThreadLocal是一個本地線程副本變量工具類,主要用于將私有線程和該線程存放的副本對象做一個映射,各個線程之間的變量互不干擾,線程之間數據隔離,可通過set、get方法操作當前線程副本數據
2、ThreadLocalMap類是ThreadLocal的靜態內部類,通過操作Entry來存儲Thread和它對應的數據。
3、Thread類比較常用,線程類內部維持一個ThreadLocalMap類實例(threadLocals)
ThreadLocalMap是Thread的成員變量,又是ThreadLocal的靜態內部類,線程數據通過ThreadLocal來操控
二、使用方式
直接創建ThreadLocal對象,定義保存的數據類型,通過set方法設置值,get方法來取出
ThreadLocal<String> t=new ThreadLocal<>(); t.set("hello"); System.out.println(t.get());注意:
1.這里只能set一次,再次在該線程set時會失效,只保留最新的值
2.ThreadLocal類中的ThreadLocalMap實體Entry繼承自WeakReference,是虛引用對象,只要發生垃圾回收GC,就會被回收掉ThreadLocalMap保存的value值
三、源碼解析
1.set方法
實際是獲取到當前的線程Thread和ThreadLocalMap對象,沒有則創建該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方法,獲取當前對象的的索引,通過線性探測法來放入對象,有沖突,往Entry數組下一個坐標位置移動
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方法
創建ThreadLocalMap,并賦值給當前的thread的成員變量
1)創建了一個Entry數組,大小為16,且該數組是繼承自WeakReference,表明在gc時會被回收掉
?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)計算散列索引位置并放入Entry數組
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一樣,進行散列時,通過hashCode值和&運算來計算當前線程threadLocal的在threadLocalMap中的位置,每個ThreadLocal對象都共享同一個nextHashCode,每個ThreadLocal的HashCode值都會在當前值上加上一個16進制數0x61c88647,保證**實現了不同的ThreadLocal對象的threadLocalHashCode基本不會相同**
&屬于位運算,為了將該threadLocal盡量充分均勻的散列到Entry數組中,且計算更高效
解決Entry數組沖突的方法時開放定址法的線性探測法,每次加上一個數,再取余,有沖突
3)設置擴容因子
setThreshold(INITIAL_CAPACITY);private void setThreshold(int len) {threshold = len * 2 / 3; }3.get方法
獲取ThreadLocalMap中當前線程對象對應的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,則創建map,并設置當前線程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中移除當前線程對象和它持有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中調用的,Thread類中維護了一個ThreadLocalMap成員變量,用于初始化該mapi
f (parent.inheritableThreadLocals != null)this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);主要是通過當前線程thread獲取ThreadLocalMap對象,初始化數組和負載因子threshold,遍歷節點加入數組。
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++;}}} }?
?
?
?
?
?
?
?
?
?
?
?
?
總結
以上是生活随笔為你收集整理的ThreadLocal原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java和Spring中线程池创建方法
- 下一篇: EasyExcel实现文件读取、导出、上