java中ThrealLocal的理解
阿新 • • 發佈:2020-08-07
目錄
java中threadlocal的理解
一、threadlocal的生命週期和ThreadLocalMap的生命週期
可以吧TreadLocal看做是一個map來使用,只不過這個map是指向當前執行緒中的threadLocals(ThreadLocalMap.class),這個threadLocals採用懶漢單例在一個執行緒中是唯一的。
Thread中的threadLocals屬性,存放的是當前執行緒在不同TreadLocal例項中的值ThreadLocalMap<TreadLocal,T>
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t);//return t.threadLocals;這裡表示只有一個ThreadLocalMap副本 if (map != null)//採用懶漢模式 map.set(this, value); else createMap(t, value);//t.threadLocals = new ThreadLocalMap(this, firstValue); }
可以通過檢視當前執行緒中的threadLocals變數,來看當前執行緒持有多少個threadLocal,以及存的值。
ThreadLocalMap例項的生命週期隨著執行緒的結束而結束,因為ThreadLocalMap例項的唯一引用只存在當前執行緒中。ThreadLocal的生命週期,需要gc來決定,因為他的引用可能存在於多個執行緒中,此引用為弱引用。
弱引用的存在,導致可能記憶體洩露。當threadlocal=null,沒有任何強引用例項指向threadLocals中的key本來指向的threadlocal例項,gc回收threadlocal,導致val永遠不會被釋放。採用以下程式碼來取代=null的操作,用remove函式來清除,threadLocals。
ThreadLocal local1 = new ThreadLocal();
local.remove();
二、ThreadLocal的作用
對於那些需要資料隔離,可以用ThreadLocal。
對於一個執行緒中的一個ThreadLocal只能存一個T型別的資料。(T為泛型)
ThreadLocal是執行緒安全的,所以可以用來封裝執行緒不安全的例項,不同執行緒之間新建立例項,保證執行緒安全。(另一種模式就是單例模式。)
三、threadlocal示例
- 比較經典的例子就是github中的PageHelper中儲存Page資訊的時候使用了ThreadLocal。考慮到資料的同步,已經一個執行緒只會順序執行sql語句。
protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal();
protected static void setLocalPage(Page page) {
LOCAL_PAGE.set(page);
}
- AopContext
在同一個類中呼叫方法,導致aop不生效。原因是aop生效是呼叫的代理類,直接呼叫被代理類會無法觸發切面。
@EnableAspectJAutoProxy(exposeProxy = true)//開啟代理配置Appcontext
AopContext.currentProxy() //獲取到當前類的代理類,可以進行強制轉換
public abstract class AopContext {
private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal("Current AOP proxy");//本質還是通過threadlocal實現
public AopContext() {
}
public static Object currentProxy() throws IllegalStateException {
Object proxy = currentProxy.get();
if (proxy == null) {
throw new IllegalStateException("Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.");
} else {
return proxy;
}
}
static Object setCurrentProxy(Object proxy) {
Object old = currentProxy.get();
if (proxy != null) {
currentProxy.set(proxy);
} else {
currentProxy.remove();
}
return old;
}
}
四、InheritableThreadLocal的擴充套件
- InheritableThreadLocal extends ThreadLocal
- 子執行緒可以使用InheritableThreadLocal可以拿到父親執行緒的變數,父執行緒無法拿到子執行緒的
- 子執行緒對變數的修改,父執行緒不可見。子執行緒修改的父執行緒那邊拷貝的副本
- init子執行緒的時候,對threadlocal中的值進行復制
class Thread{
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) {
......略
Thread parent = currentThread();
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
......略
}
}
- 和threallocal相比,只有map的引用不一樣。