JUC---ThreadLocal源碼解析(JDK13)

java.util.concurrent包系列文章
JUC—ThreadLocal源碼解析(JDK13)
JUC—ThreadPoolExecutor線程池源碼解析(JDK13)
JUC—各種鎖(JDK13)
JUC—原子類Atomic*.java源碼解析(JDK13)
JUC—CAS源碼解析(JDK13)
JUC—ConcurrentHashMap源碼解析(JDK13)
JUC—CopyOnWriteArrayList源碼解析(JDK13)
JUC—併發隊列源碼解析(JDK13)
JUC—多線程下控制併發流程(JDK13)
JUC—AbstractQueuedSynchronizer解析(JDK13)


ThreadLocal -> 線程本地變量

  • 強調同一個請求內(同一個線程內)不同方法間的共享
  • 讓某個需要用到的對象在線程間隔離(每個線程都有自己獨有自己獨立的對象)

ThreadLocal的優點

  • 達到線程安全
  • 不需要加鎖
  • 提高執行效率 高效利用內存
  • 節省開銷 避免傳參的麻煩

Thread、ThreadLocal、ThreadLocalMap三者的關係

每個Thread對象中都持有一個ThreadLocalMap對象,可以看做是一個Map,key就是ThreadLocal,value就是放進去的對象。

在這裏插入圖片描述

所謂線程本地變量,其實就是把值存儲在當前線程的:

在這裏插入圖片描述
在這裏插入圖片描述

ThreadLocal的重要方法

public void set(T value) {
    // 獲取當前線程
    Thread t = Thread.currentThread();
    // 獲取當前線程的ThreadLocalMap 
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        // 把當前ThreadLocal作爲key
        map.set(this, value);
    } else {
        // map爲空就初始化並設置值
        createMap(t, value);
    }
}

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}
public T get() {
     // 獲取當前線程
    Thread t = Thread.currentThread();
    // 獲取當前線程的ThreadLocalMap 
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        // 把當前ThreadLocal作爲key,差U型你
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    // map爲空說明還未初始化
    return 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);
    }
    if (this instanceof TerminatingThreadLocal) {
        TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
    }
    return value;
}

protected T initialValue() {
    return null;
}

public void remove() {
    // 獲取當前線程的ThreadLocalMap 
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null) {
        // m不爲空則移除元素
        m.remove(this);
    }
}

從get()、set()、remove()方法都可以看到,就是以當前線程爲變量爲key存儲線程本地變量。每一個線程都有自己的變量值。從而沒有線程安全問題。線程退出的時候必須調用remove()方法,防止溢出。

業務場景:在攔截器preHandle()方法中驗證用戶身份,驗證通過,則調用set()方法把當前用戶信息存入ThreadLocal。放行當前請求,當前請求進入業務系統,執行了各種業務處理之後,在攔截器的postHandle()和afterCompletion()方法中一定要調用remove()方法清楚當前線程本地變量。

在這裏插入圖片描述

ThreadLocal在Spring中的典型例子:RequestContextHolder、DateTimeContextHolder

public abstract class RequestContextHolder  {

   private static final boolean jsfPresent =
         ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader());

   private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
         new NamedThreadLocal<>("Request attributes");

   private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
         new NamedInheritableThreadLocal<>("Request context");
         
         、、、省略、、、
         
         @Nullable
        public static RequestAttributes getRequestAttributes() {
           RequestAttributes attributes = requestAttributesHolder.get();
           if (attributes == null) {
              attributes = inheritableRequestAttributesHolder.get();
           }
           return attributes;
        }
}         

  • 我的公衆號:Coding摳腚
  • 一個被電焊耽誤的Java程序員。偶爾發發自己最近學到的乾貨。學習路線,經驗,技術分享。技術問題交流探討。
    Coding摳腚
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章