1. 程式人生 > 實用技巧 >java中ThrealLocal的理解

java中ThrealLocal的理解

目錄

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示例

  1. 比較經典的例子就是github中的PageHelper中儲存Page資訊的時候使用了ThreadLocal。考慮到資料的同步,已經一個執行緒只會順序執行sql語句。
 protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal();
 protected static void setLocalPage(Page page) {
         LOCAL_PAGE.set(page);
 }
  1. 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的擴充套件

  1. InheritableThreadLocal extends ThreadLocal
  2. 子執行緒可以使用InheritableThreadLocal可以拿到父親執行緒的變數,父執行緒無法拿到子執行緒的
  3. 子執行緒對變數的修改,父執行緒不可見。子執行緒修改的父執行緒那邊拷貝的副本
  4. 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);
......略
    }
}
  1. 和threallocal相比,只有map的引用不一樣。