<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          源碼分析--ThreadLocal(圖解)

          共 8997字,需瀏覽 18分鐘

           ·

          2021-04-17 19:13

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

          想清楚ThreadLocal源碼的原理,那么先搞明白 ThreadLocalMap ,很關(guān)鍵

          1.首先我們知道Thread 里面有一個(gè)ThreadLocalMap ;

           public class Thread implements Runnable {
              ThreadLocal.ThreadLocalMap threadLocals = null;
           }

          2.ThreadLocalMap里面有包含了一個(gè)成員變量Entry[] table。

          static class ThreadLocalMap {
              
               private Entry[] table;
               
              //Entry繼承了WeakReference<ThreadLocal<?>>, 說明Entry 持有一個(gè)指向ThreadLocal的弱引用。
              //弱引用,就是如果一個(gè)對(duì)象只有弱引用指向它,下一次JVM垃圾回收的時(shí)候一定會(huì)被回收調(diào)。
              static class Entry extends WeakReference<ThreadLocal<?>> {
                  //這個(gè)value就是存放我們的數(shù)據(jù)
                   Object value;
                  
                  Entry(ThreadLocal<?> k, Object v) {
                      super(k);
                      value = v;
                  }
              }
              ///...省略部分源碼
            }

          上面的1,2代碼反應(yīng)了如下關(guān)系

          • 一個(gè)Thread里面包含了一個(gè)ThreadLocalMap

          • 一個(gè)ThreadLocalMap包含了一個(gè)table

          • table是一個(gè)Entry數(shù)組

          • Entry數(shù)組有一個(gè)value存放數(shù)據(jù),reference弱引用可以指向某個(gè)ThreadLocal

          3、ThreadLocalMap包含了一個(gè)Entry[] table, 其實(shí)ThreadLocalMap是底層數(shù)據(jù)結(jié)構(gòu)就是一個(gè)Entry數(shù)組

          ThreadLocalMap是一個(gè)Map,Entry代表一個(gè)哈希槽。

          Entry的key(鍵)其實(shí)就是reference, 而value(值)就是上面的value.

          ThreadLocalMap存值原理:

          1.就是通過Entry的 key的Hash值計(jì)算出Index

          2.找到數(shù)組的Index位置,如果該位置為空就存放Entry

          3.不為空則Index ++,直到找到一個(gè)空的位置存放 (這里還有一個(gè)擴(kuò)容的問題,暫不討論)


          ThreadLocalMap取值原理:

          1.就是通過 key的Hash值計(jì)算出Index

          2.找到Index的位置Entry,再對(duì)比一下Entry的key和需要查找的key是不是相等,相等則取出value

          3.不相等則Index++,然后重復(fù)第2步驟


          總結(jié):每個(gè)Thread 對(duì)象里面有一個(gè)ThreadLocalMap。ThreadLocalMap是一個(gè)Map, key是ThreadLocal類型的數(shù)據(jù), value存放數(shù)據(jù)


          4.TheadLocal 的set方法

          public class ThreadLocal<T> {
              public void set(T value) {
                  //1.獲取當(dāng)前線程的ThreadLocalMap(就是上面說的ThreadLocalMap)
                  Thread t = Thread.currentThread();
                  ThreadLocalMap map = getMap(t);
                  if (map != null)
                      // ThreadLocalMap是一個(gè)Map, key是ThreadLocal類型的數(shù)據(jù), value存放數(shù)據(jù)
                      // 2.map 不為空, Map的key是this對(duì)象本身,value是我們存放的數(shù)據(jù)。
                     //可能會(huì)疑惑,為什么key存放this?下面再詳細(xì)說明
                      map.set(this, value);
                  else
                  //3.map 為空,為當(dāng)前線程創(chuàng)建一個(gè)Map
                      createMap(t, value);
              }
          }


          源碼為什么key存放this?

          1.一個(gè)Thread有一個(gè)ThreadLocalMap,而ThreadLocalMap可以存放多個(gè)Entry { “key”:ThreadLocal :“value”: object }


          2.如下測試代碼所示,當(dāng)我們?cè)诰€程A里面操作threadLocal1.set(1),

          其實(shí)就是在線程A自己的ThreadLocalMap的 Entry{ “key”:threadLocal1 :“value”: 1}。

          反之, 線程B就是線程B自己的ThreadLocalMap 的 Entry{ “key”:threadLocal1 :“value”: 2}。

          public class ThreadLocalTest {

              static ThreadLocal<Integer> threadLocal1 = new ThreadLocal<>();

              static ThreadLocal<Integer> threadLocal2 = new ThreadLocal<>();

              public static void main(String[] args) {

                  Thread threadA = new Thread(new Runnable() {
                      @Override
                      public void run() {
                          try{
                              threadLocal1.set(1);
                              threadLocal2.set(2);
                          }finally {
                              threadLocal1.remove();
                              threadLocal2.remove();
                          }
                      }
                  });

                  Thread threadB = new Thread(new Runnable() {
                      @Override
                      public void run() {
                          try{
                              threadLocal1.set(1);
                              threadLocal2.set(2);
                          }finally {
                              threadLocal1.remove();
                              threadLocal2.remove();
                          }
                      }
                  });

                  threadA.start();
                  threadB.start();
              }
          }

          最后數(shù)據(jù)存放圖如下(table里面entry存放數(shù)據(jù)的位置只是假設(shè),threadLocal1 存放的位置還是通過它的Hash值求出的Index所得 ):

          5.ThreadLocal的get方法( 如果看懂上面的set方法,這里就很簡單了)

          public T get() {
               //1.獲取當(dāng)前線程的ThreadLocalMap(就是上面說的ThreadLocalMap)
              Thread t = Thread.currentThread();
              ThreadLocalMap map = getMap(t);
              if (map != null) {
                  //map 不為空,根據(jù)this獲取Entry 
                  ThreadLocalMap.Entry e = map.getEntry(this);
                 //e不為空,返回e.value
                  if (e != null) {
                      @SuppressWarnings("unchecked")
                      T result = (T)e.value;
                      return result;
                  }
              }
              return setInitialValue();
          }

          private T setInitialValue() {
              //initialValue() 就是返回了一個(gè)null
              T value = initialValue();
              Thread t = Thread.currentThread();
              //獲取當(dāng)前線程的ThreadLocalMap
              ThreadLocalMap map = getMap(t);
              if (map != null)
                  //map 不為空,設(shè)置值為null
                  map.set(this, value);
              else
                  //map 為空,創(chuàng)建一個(gè)map,并設(shè)置值為null
                  createMap(t, value);
              return value;
          }

          6.ThreadLocal的數(shù)據(jù)用完后記得使用 threadLocal.remove()移除數(shù)據(jù),不然在某種情況下可能會(huì)導(dǎo)致內(nèi)存泄漏(如圖下描述)

          ————————————————

          版權(quán)聲明:本文為CSDN博主「略。。。。」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。

          原文鏈接:

          https://blog.csdn.net/fsdf8sad7/article/details/113939691





          鋒哥最新SpringCloud分布式電商秒殺課程發(fā)布

          ??????

          ??長按上方微信二維碼 2 秒





          感謝點(diǎn)贊支持下哈 

          瀏覽 35
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  韩国精品一区二区三区 | AV黄色在线观看 | 93精品视频 | 夜夜爽夜夜 | 99久久久久 |