* This class provides thread-local variables. These variables differ from * their normal counterparts in that each thread that accesses one (via its * {@code get} or {@code set} method) has its own, independently initialized
* copy of the variable. {@code ThreadLocal} instances are typically private
* static fields in classes that wish to associate state with a thread (e.g., * a user ID or Transaction ID).複製代碼
ThreadLocal類用來提供線程內部的局部變量。這種變量在多線程環境下訪問(經過get或set方法訪問)時能保證各個線程裏的變量相對獨立於其餘線程內的變量。ThreadLocal實例一般來講都是private static
類型的,用於關聯線程和線程的上下文。java
ThreadLocal的做用是提供線程內的局部變量,這種變量在線程的生命週期內起做用,減小同一個線程內多個函數或者組件之間一些公共變量的傳遞的複雜度
緩存
ThreadLocal不是同步機制,也不解決共享對象的多線程競態條件問題。安全
ThreadLocal用來輔助平衡效率與線程的資源分配。bash
ThreadLocal會爲每個線程提供一個獨立的變量副本,從而隔離了多個線程對數據的併發訪問衝突。由於每個線程都擁有本身的變量副本,從而也就沒有必要對該變量進行同步了。多線程
ThreadLocal併發
set(T):保存對象jvm
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}複製代碼
get():當某個線程初次調用ThreadLocal.get方法時,就會調用initialValue()來獲取初始值,不然返回由當前執行線程在調用set(T)的最新值,該方法避免了將這個對象做爲參數傳遞的麻煩。ide
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();
}複製代碼
(1)獲取當前線程
函數
(2)根據當前線程獲取一個map 性能
(3)若是獲取的map不爲空,則在map中以ThreadLocal的引用做爲key來在map中獲取對應的value e,不然轉到(5)
(4)若是e不爲null,則返回e.value,不然轉到(5)
(5)Map爲空或者e爲空,則經過initialValue函數獲取初始值value,而後用ThreadLocal的引用和value做爲firstKey和firstValue建立一個新的Map
remove():將當前線程的ThreadLocal綁定的值刪除
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}複製代碼
initialValue():用於設置 ThreadLocal 的初始值,默認返回 null,該函數是protected類型的,一般該函數都會以匿名內部類的形式被重載,以指定初始值
protected T initialValue() {
return null;
}複製代碼
getMap(Thread):獲取線程的ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}複製代碼
createMap(Thread,T):初始化線程的threadLocals,而後設定key-value
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}複製代碼
createInheritedMap(ThreadLocalMap):
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}複製代碼
nextHashCode():
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}複製代碼
setInitialValue():
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}複製代碼
JDK1.3:ThreadLocal<T>視爲包含了Map<Thread,T>對象,其中保存了特定於該線程的值。而後用線程的ID做爲Map的key,實例對象做爲Map的value,這樣就能達到各個線程的值隔離的效果
JDK1.8:每一個Thread維護一個ThreadLocalMap映射表,這個映射表的key是ThreadLocal實例自己,value是真正須要存儲的Object。
JDK1.8優點
每一個線程中都有一個本身的ThreadLocalMap類對象,能夠將線程本身的對象保持到其中,各管各的,線程能夠正確的訪問到本身的對象。
java.lang.Thread:
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;複製代碼
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}複製代碼
ThreadLocal不能繼承父線程的ThreadLocal的內容, InheritableThreadLocal類繼承於ThreadLocal類,InheritableThreadLocal變量值會自動傳遞給全部子線程,在父子線程之間傳遞數據。 建立一個線程時若是保存了全部 InheritableThreadLocal 對象的值,那麼這些值也將自動傳遞給子線程。若是一個子線程調用 InheritableThreadLocal 的 get() ,那麼它將與它的父線程看到同一個對象。爲保護線程安全性,您應該只對不可變對象(一旦建立, 其狀態就永遠不會被改變的對象)使用InheritableThreadLocal ,由於對象被多個線程共享。 InheritableThreadLocal合適用於把數據從父線程傳到子線程, 很例如用戶標識(user id)或事務標識(transaction id),但不能是有狀態對象,例如 JDBC Connection 。
private static ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<>();
private static InheritableThreadLocal<Integer> inheritableThreadLocal =
new InheritableThreadLocal<>();
@Test
public void inheritableThreadLocal() {
// 父線程
integerThreadLocal.set(1);
inheritableThreadLocal.set(1);
//結果:pool-1-thread-1:null/1
threadFactory.newThread(() -> System.out.println(Thread.currentThread().getName() + ":"
+ integerThreadLocal.get() + "/"
+ inheritableThreadLocal.get())).start();
}複製代碼
Reference(引用)
相似「Object obj =new Object()」這類的引用,只要強引用還存在,垃圾收集器永遠不會回收掉被引用的對象。強引用是Java的默認引用實現, 它會盡量長時間的存活於JVM內, 當沒有任何對象指向它時, GC執行後也不會被回收。若是一個對象具備強引用,那就相似於必不可少的生活用品, 垃圾回收器毫不會回收它。當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤, 使程序異常終止,也不會靠隨意回收具備強引用的對象來解決內存不足問題。JVM 系統採用 Finalizer 來管理每一個強引用對象 , 並將其被標記要清理時加入 ReferenceQueue, 並逐一調用該對象的 finalize() 方法
Object obj = new Object();
Object strongReference = obj;
Assertions.assertSame(obj, strongReference);
obj = null;
System.gc();
Assertions.assertNull(obj);
Assertions.assertNotNull(strongReference);複製代碼
Finalizer是FinalReference的子類,該類被final修飾,不可再被繼承,JVM實際操做的是Finalizer。當一個類知足實例化FinalReference的條件時,JVM會調用Finalizer.register()進行註冊。
Object obj = new Object();
SoftReference<Object> softReference = new SoftReference<>(obj);
Assertions.assertNotNull(softReference.get());
obj = null;
System.gc();
Assertions.assertNull(obj);
// SoftReference 只有在 jvm OutOfMemory 以前纔會被回收, 因此它很是適合緩存應用
Assertions.assertNotNull(softReference.get());複製代碼
用來描述非必須對象的,但它的強度比軟引用要弱一些,被弱引用關聯的對象只能生存到下一次垃圾收集發生以前。當垃圾收集器工做時,不管當前內存是否足夠,都會回收掉只被弱引用關聯的對象。
Object obj = new Object();
WeakReference<Object> weakReference = new WeakReference<>(obj);
Assertions.assertSame(obj, weakReference.get());
obj = null;
System.gc();
Assertions.assertNull(weakReference.get());
Map<Object, Object> weakHashMap = new WeakHashMap<>();
Object key = new Object();
Object value = new Object();
weakHashMap.put(key, value);
Assertions.assertTrue(weakHashMap.containsValue(value));
key = null;
System.gc();
Thread.sleep(1000);
// 一旦沒有指向 key 的強引用, WeakHashMap 在 GC 後將自動刪除相關的 entry
Assertions.assertFalse(weakHashMap.containsValue(value));複製代碼