溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

ThreadLocal的基本定義是什么

發(fā)布時(shí)間:2021-12-17 14:30:06 來源:億速云 閱讀:144 作者:柒染 欄目:大數(shù)據(jù)

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(guān)ThreadLocal的基本定義是什么,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

基本定義及解讀

官方釋義: http://docs.oracle.com/javase/8/docs/api/
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. 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).

  • 首先,每個(gè)線程都有變量的一個(gè)本地獨(dú)立副本,保證線程之間的數(shù)據(jù)不會(huì)互相影響

  • 可以通過重寫initialValue()方法實(shí)現(xiàn)ThreadLocal的默認(rèn)初始值

    protected static final ThreadLocal<String> TL_EXAMPLE = 
                new ThreadLocal<String>() {  @Override
      protected String initialValue() {      return "default";
      }
    };
  • 為什么說明中建議定義為靜態(tài)static方法呢?不了解ThreadLocal原理的同學(xué)可能就糊涂了,既然是需要滿足多線程并發(fā)的,怎么會(huì)定義為一個(gè)靜態(tài)的類成員變量呢?
    只要大家看一下ThreadLocal的源碼就了解了,它有個(gè)靜態(tài)內(nèi)部類叫ThreadLocalMap<ThreadLocal, Object>, 此Map在Thread類中被定義為了一個(gè)類成員變量,即每個(gè)Thread線程中都有一個(gè)獨(dú)立ThreadLocalMap副本,它的值只能被當(dāng)前線程讀取和修改
    想像一下某個(gè)類中定義了多個(gè)ThreadLocal<?>變量,在當(dāng)前線程中通過ThreadLocalMap.get(ThreadLocal)獲取到相應(yīng)的變量副本。
    所以ThreadLocal變量本身不是副本,你可以把他當(dāng)成一個(gè)代理,而ThreadLocalMap中存放了線程內(nèi)的一個(gè)一個(gè)線程副本,ThreadLocal只是ThreadLocalMap內(nèi)弱引用的Key(在ThreadLocal對(duì)象失效時(shí)可以及時(shí)的清理ThreadLocalMap)。
    這也回答了為什么ThreadLocal可以定義為static, 它只是Map中的Key而已,不同線程的Map副本獲取同一個(gè)Key的值完全不會(huì)沖突。

  類ThreadLocal:  
  public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);        if (e != null)            return (T)e.value;
    }    return setInitialValue();
  }  /**
  * 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 {...}

  類Thread:  /* ThreadLocal values pertaining to this thread. This map is maintained
   * by the ThreadLocal class. */
   ThreadLocal.ThreadLocalMap threadLocals = null;

Thread1 : 
       ThreadLocalMap1 : 
             <ThreadLocal1, String>
             <ThreadLocal2, Integer>Thread2 :
       ThreadLocalMap2 :             <ThreadLocal1, String>
             <ThreadLocal2, Integer>
  • 繼續(xù)延伸出一個(gè)問題: ThreadLocal類本身是線程安全的么?
    通過源碼看到不管是get,set還是createMap都沒有做任何的同步或者并發(fā)鎖。答案是安全的,因?yàn)閷?shí)現(xiàn)都是基于當(dāng)前線程的。

線程池與ThreadLocal變量的初始化

在線程池復(fù)用的情況下,若ThreaLocal數(shù)據(jù)沒有被清理掉,會(huì)被后面的請(qǐng)求復(fù)用然后拿到被你修改過的值!
之前在實(shí)現(xiàn)日志上下文LogContext的時(shí)候碰到了類似問題:

  1. 請(qǐng)求A進(jìn)入Controller中, 開啟線程A,LogContext中記錄了大量的ThreadLocal中間變量值,在請(qǐng)求響應(yīng)結(jié)束后,請(qǐng)求線程A回歸線程池;

  2. 請(qǐng)求B進(jìn)入Controller中,復(fù)用線程A,LogContext會(huì)在之前變量值的基礎(chǔ)上繼續(xù)添加信息,這樣的日志信息成了疊加的了。

不管是基于自己實(shí)現(xiàn)的線程池,還是應(yīng)用服務(wù)器(如Tomcat)的線程池,都需要小心這一點(diǎn)!
標(biāo)準(zhǔn)或者規(guī)范做法是在線程變量使用完畢之后,或者finally代碼塊中調(diào)用 threadLocalVariable.remove() 移除,以防被其他線程復(fù)用。

上述就是小編為大家分享的ThreadLocal的基本定義是什么了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI