ThreadLocal原理解析

在分析ThreadLocal之前,首先我們提出三個問題,后續(xù)會圍繞這三個問題解析ThreadLocal的原理。

  • 什么是ThreadLocal?
  • ThreadLocal怎么用?
  • ThreadLocal的原理是什么?為什么能保證每個線程的數(shù)據(jù)都不受其他線程干擾?

1.什么是ThreadLocal?
  • 首先拋個定義,ThreadLocal為解決多線程程序的并發(fā)問題提供了一種新的思路。使用這個工具類可以很簡潔地編寫出優(yōu)美的多線程程序,ThreadLocal并不是一個Thread,而是Thread的局部變量。
  • 翻譯成能聽懂的話就是可以通過ThreadLocal保證同一線程的某些數(shù)據(jù)不被其他線程干擾,影響。
2.ThreadLocal怎么用?

ThreadLocal的使用及其簡單,常用的方法只有兩個,get(),set();

   ThreadLocal<String> threadLocal = new ThreadLocal<>();
   threadLocal.set(TAG);
   threadLocal.get();

舉個使用ThreadLocal的真實場景

  • ? ? ? ?2.1.我們正常的項目中有很多復(fù)雜的邏輯,首先我們會分割不同的業(yè)務(wù),并單獨開啟多個線程去執(zhí)行對應(yīng)的業(yè)務(wù)。
  • ? ? ? ?2.2. 每個業(yè)務(wù)需要打印本業(yè)務(wù)的全量日志。正常可通過Log.d(TAG,Message)來打印;每條的日志的TAG都需要一條條的設(shè)置。
  • ? ? ? ?2.3. 其實該場景可以使用ThreadLocal.set(TAG),在Log工具類中使用Log.d(ThreadLoacl.get(),Message)即可,省去了頻繁設(shè)置TAG的操作。
3. ThreadLocal的原理是什么?為什么能保證每個線程的數(shù)據(jù)都不受其他線程干擾?
  • 原理可以理解為每一個線程類內(nèi)都有一個ThreadLocalMap類型的局部變量,該變量中設(shè)有一個ThreadLocalMap.Entry[]數(shù)組,數(shù)組中的每一個ThreadLocalMap.Entry對象中存的是k/v(ThreadLocal/Value)鍵值對,而我們存儲的數(shù)據(jù)就在這個鍵值對中。
  • 正常的使用中主要分為3步
      1. new ThreadLocal<>();
      1. threadLocal.set(Object);
      1. threadLocal.get();
    • 下文主要從這三步進行展開分析。

1. ThreadLocal<String> threadlocal = new ThreadLocal<>();

  • 該操作調(diào)用了ThreadLocal的構(gòu)造函數(shù),除了將ThreadLocal內(nèi)部屬性threadlocalHashcode自增了0x61c88647外,并沒有做其他的操作。

2. 當(dāng)調(diào)用ThreadLocal.set(Object),該操作中主要涉及了4個步驟。

1. Thread t = Thread.currentThread();//獲取當(dāng)前線程

2. ThreadLocalMap map = ThreadLocal.getMap(t);//獲取當(dāng)前線程中的ThreadLocalMap對象。

3. map!=null 則調(diào)用 map.set(ThreadLocal,Value);
       獲取ThreadLocalMap內(nèi)部Entry[]數(shù)組,通過ThreadLocal.threadLocalHashCode & (len-1)計算將要存放的位置索引。
       循環(huán)向后判斷沖突,如果Entry[i]存在數(shù)據(jù),且key和將要存儲的數(shù)據(jù)相同,則替換value,如果key為null則認(rèn)為當(dāng)前數(shù)據(jù)是臟數(shù)據(jù),啟動環(huán)形清理。
       沒有沖突則 new ThreadLocalMap.Entry(key, value)添加到Entry[i]并啟動清理,如果清理不成功并且Entry.len>len*0.75則啟動擴容rehash(),擴大到原來大小的2倍。

4. map == null 則調(diào)用 ThreadLocal.createMap(Thread,Value);
       Thread.threadLocalMap = new ThreadLocalMap(ThreadLocal,Value);
       ThreadLocalMap.Entry[] entry = new ThreadLocalMap.Entry[16]//初始化Entry[]數(shù)組。
       通過 i = ThreadLocal.threadLocalHashCode & (len - 1)計算在ThreadLocalMap.Entry[]數(shù)組中的存儲索引。
       ThreadLocalMap.Entry[i] = new ThreadLocalMap.Entry(k,v);

3. 當(dāng)調(diào)用ThreadLocal.get(),該操作中也主要涉及了4個步驟

1. Thread t = Thread.currentThread();

2. ThreadLocalMap map = t.threadLocals;//獲取當(dāng)前線程中的ThreadLocalMap對象。

3. map != null map.getEntry(ThreadLocal);
      通過 i = ThreadLocal.threadLocalHashCode & (len - 1)計算當(dāng)前ThreadLocal數(shù)據(jù)存儲的位置。
      Entry e = ThreadLocalMap.Entry[i] ,e!=null且e.get()和當(dāng)前ThreadLocal key相等則返回 e,否則循環(huán)后續(xù)的數(shù)據(jù).
      如有key相同的則返回,如有臟數(shù)據(jù)(key==null的數(shù)據(jù)為臟數(shù)據(jù))則清理。

4. map == null ThreadLocal.setInitialValue()
      t.threadLocals = new ThreadLocalMap(ThreadLocal, null);
      ThreadLocalMap.Entry[] entry = new ThreadLocalMap.Entry[16]//初始化Entry[]數(shù)組。
      通過 i = ThreadLocal.threadLocalHashCode & (len - 1)計算在ThreadLocalMap.Entry[]數(shù)組中的存儲索引。
      ThreadLocalMap.Entry[i] = new ThreadLocalMap.Entry(k,null);

總結(jié)

    1. 每一個Thread 都維護了一個類型為ThreadLocalMap名叫threadLocals的對象,每次get獲取的時,均通過Thread.currentThread()獲取了當(dāng)前線程的ThreadLocalMap,因此做到了不同線程之間互不干擾。
    1. 當(dāng)調(diào)用ThreadLocal.set()方法時,會通過當(dāng)前線程獲取到線程中的ThreadLocalMap對象。
      然后獲取到ThreadLocalMap.Entry[]數(shù)組,最后通過hash計算出數(shù)組中的存放位置,然后new ThreadLocalMap.Entry(ThreadLocal<?> k, Object v)存放在數(shù)組內(nèi),該索引下有重復(fù)key則替換,key==null則啟動環(huán)形清理,沒數(shù)據(jù)則插入。
    1. 當(dāng)調(diào)用ThreadLocal.get()方法時,ThreadLocal會通過線程獲取當(dāng)前線程中的ThreadLocalMap對象.
      然后獲取到ThreadLocalMap.Entry[]數(shù)組,并通過hash計算出數(shù)組中的存放位置.
      由ThreadLocalMap.Entry[i]獲取到數(shù)據(jù),判斷key相同則返回,key == null則清理。
      如果獲取ThreadLocalMap == null,則啟動初始化,通過ThreadLocalMap創(chuàng)建長度為16的ThreadLocalMap.Entry[],并存value 為 null的一條數(shù)據(jù)到Entry[]數(shù)組下。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容