java同一線程進(jìn)行多Java實(shí)例對象調(diào)用中,可以使用TheadLoca進(jìn)行的數(shù)據(jù)傳遞。傳遞的數(shù)據(jù)在每個(gè)線程內(nèi)部獨(dú)享。
1、使用ThreadLocal。
public static void main(String[] args) { ThreadLocal<String> threadLocal = new ThreadLocal<String>(); ThreadLocal<Integer> threadInt = new ThreadLocal<Integer>(); System.out.PRintln(threadLocal.get() + "--" + threadInt.get()); threadLocal.set("first"); threadInt.set(1); System.out.println(threadLocal.get() + "--" + threadInt.get()); threadLocal.set("second"); threadInt.set(2); System.out.println(threadLocal.get() + "--" + threadInt.get()); System.out.println(threadLocal.get() + "--" + threadInt.get()); }
2、ThreadLocal類:
初始化ThreadLoca類時(shí),構(gòu)造函數(shù)為空
/** * ThreadLocals rely on per-thread linear-probe hash maps attached * to each thread (Thread.threadLocals and * inheritableThreadLocals). The ThreadLocal objects act as keys, * searched via threadLocalHashCode. This is a custom hash code * (useful only within ThreadLocalMaps) that eliminates collisions * in the common case where consecutively constructed ThreadLocals * are used by the same threads, while remaining well-behaved in * less common cases. */ private final int threadLocalHashCode = nextHashCode();/** * Creates a thread local variable. */ public ThreadLocal() { }
其中變量threadLocalHashCode為不可變的final類型,表明該ThreadLocal實(shí)例中的該屬性一直不能改變,該變量賦值語句為:
/** * The next hash code to be given out. Updated atomically. Starts at * zero. */ private static AtomicInteger nextHashCode = new AtomicInteger(); /** * The difference between successively generated hash codes - turns * implicit sequential thread-local IDs into near-optimally spread * multiplicative hash values for power-of-two-sized tables. */ private static final int HASH_INCREMENT = 0x61c88647; /** * Returns the next hash code. */ private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); }
使用了靜態(tài)的類AtomicInteger,提供一個(gè)原子性的相加操作,保證每個(gè)線程之間的調(diào)用不會沖突,靜態(tài)AtomicInteger保證每個(gè)ThreadLoca的初始化都不會得到相同的值。其中添加量為:0x61c88647(為什么是這個(gè)值?)。
ThreadLocal提供了可供調(diào)用的方法:
public T get()
public void set(T value)
public void remove()
先看set方法:
/** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */ public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
使用當(dāng)前Thread當(dāng)做Key,獲取ThreadLocalMap。
/** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @return the map */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
最終返回的是Thread中的變量,threadLocals。
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null;
Thread類中的變量,threadLocals為每個(gè)線程私有的屬性。同一個(gè)線程中的每一個(gè)ThreadLocal調(diào)用getMap(Thread t),都會得到相同的值, ThreadLocalMap:
/** * ThreadLocalMap is a customized hash map suitable only for * maintaining thread local values. No Operations are exported * outside of the ThreadLocal class. The class is package private to * allow declaration of fields in class Thread. To help deal with * very large and long-lived usages, the hash table entries use * WeakReferences for keys. However, since reference queues are not * used, stale entries are guaranteed to be removed only when * the table starts running out of space. */ static class ThreadLocalMap { /** * The initial capacity -- MUST be a power of two. */ private static final int INITIAL_CAPACITY = 16; /** * The table, resized as necessary. * table.length MUST always be a power of two. */ private Entry[] table; /** * The number of entries in the table. */ private int size = 0; /** * The next size value at which to resize. */ private int threshold; // Default to 0
ThreadLocalMap看名稱以為Map的子類,其實(shí)為一個(gè)普通類,內(nèi)部有一個(gè)數(shù)組 Entry[] table;
在ThreadLocal中的Set方法中,若Thread對象中的threadLocals為null( Thread對象中該屬性默認(rèn)為null),則調(diào)用createMap方法:
/** * Create the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @param firstValue value for the initial entry of the map * @param map the map to store. */ void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
new 出一個(gè)ThreadLocalMap對象,賦值給Thread的threadLocals屬性,看構(gòu)造方法:
/** * Construct a new map initially containing (firstKey, firstValue). * ThreadLocalMaps are constructed lazily, so we only create * one when we have at least one entry to put in it. */ ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); }
構(gòu)造時(shí)需要傳入兩個(gè)參數(shù), ThreadLocal與 Object作為key-value, table數(shù)組初始化為16個(gè), threadLocalHashCode為傳入的ThreadLocal前面講過的不變值的屬性, 該值與15進(jìn)行與的操作,得到ThreadLocal對應(yīng)數(shù)組的index,同時(shí)設(shè)定該數(shù)組中index為new Entry對象。
/** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ static class Entry extends WeakReference<ThreadLocal> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal k, Object v) { super(k); value = v; } }
Entry繼承了WeakReference, 使用一個(gè)ThreadLocal 的Weak引用,關(guān)于Java中的4種引用:
a.強(qiáng)引用(strong reference): Object obj = new Object(); obj就為一個(gè)強(qiáng)引用,obj=null后, 該對象可能會被JVM回收
b.軟引用(SoftReference):
SoftReference<Object> softref = new SoftReference<Object>(obj);
obj = null;
在內(nèi)存不夠用的時(shí)候,才會回收軟引用的對象。
c.弱引用(WeakReference):
WeakReference<Object> weakRef = new WeakReference<Object>(obj);
obj = null;
該new出來的對象沒有強(qiáng)引用連接時(shí),下一次GC時(shí),就會回收該對象。
d.虛引用(PhantomReference),它保存ReferenceQueue中的軌跡
引自:http://m.survivalescaperooms.com/mengdd/p/3298852.html
此處使用弱引用,表明該ThreadLocal的Key轉(zhuǎn)換成弱引用的對象。此處使用弱引用的好處不影響該:ThreadLocal k 的生命周期,若程序運(yùn)行后,該ThreadLocal k連接的對象沒有強(qiáng)引用后,該Entry中的Key很快會被回收掉,變?yōu)閚ull,這樣的話 (private Entry[] table;)中的該index的位置就可以被再次使用了。
其中 setThreshold(),設(shè)定數(shù)組的閥值為 len * 2 / 3, 初始化時(shí)為10.
ThreadLocalMap中的set方法:
/** * Set the value associated with key. * * @param key the thread local object * @param value the value to be set */ 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(); }
從key.threadLocalHashCode計(jì)算得到的第i個(gè)元素
若不為null,開始向后遍歷:
若Entry中的k為Key,并且value也相等,則不做操作,返回。
若Entry中的key為null,表明該Entry可以進(jìn)行下一步操作(replaceStaleEntry方法),可能是替換該Entry,也可能是查其它....
若為null,
new Entry,賦值到數(shù)組中,
rehash方法中,判斷是否擴(kuò)容數(shù)據(jù), *2
get方法首先從ThreadLocalMap中查找,
/** * Get the entry associated with key. This method * itself handles only the fast path: a direct hit of existing * key. It otherwise relays to getEntryAfterMiss. This is * designed to maximize performance for direct hits, in part * by making this method readily inlinable. * * @param key the thread local object * @return the entry associated with key, or null if no such */ private Entry getEntry(ThreadLocal key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e); }
若數(shù)組中的第i個(gè)不符合,則調(diào)用getEntryAfterMiss,向后遍歷。
參考:
http://m.survivalescaperooms.com/digdeep/p/4510875.html
http://wangxinchun.VEvb.com/blog/1884228
新聞熱點(diǎn)
疑難解答
圖片精選