<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      深入理解ThreadLocal

      在java的多線程模塊中,ThreadLocal是經常被提問到的一個知識點 ,因此只有理解透徹了,不管怎么問,都能游刃有余。

      本文主要從以下幾個角度來分析理解

      • ThreadLocal是什么
      • ThreadLocal怎么用
      • ThreadLocal源碼分析
      • ThreadLocal內存泄漏問題

      PS:以下源碼均基于jdk8。

      1. ThreadLocal是什么?

      從名字我們就可以看到ThreadLocal 叫做本地線程變量,意思是說,ThreadLocal 中填充的的是當前線程的變量,該變量對其他線程而言是封閉且隔離的,ThreadLocal 為變量在每個線程中創建了一個副本,這樣每個線程都可以訪問自己內部的副本變量。

      從字面意思很容易理解,但是實際角度就沒那么容易了,作為一個面試常問的點,使用場景也是很豐富。

      • 在進行對象跨層傳遞的時候,使用ThreadLocal可以避免多次傳遞,打破層次間的約束。
      • 線程間數據隔離
      • 進行事務操作,用于存儲線程事務信息。
      • 數據庫連接,Session會話管理。

      現在相信你已經對ThreadLocal有一個大致的認識了,下面我們看看如何用?

      2. ThreadLocal怎么用?

      下面讓我們來看一個例子:

      import java.util.stream.IntStream;
      
      public class ThreadLocalTest {
      
          public static void main(String[] args) {
      
              ThreadLocal<String> local = new ThreadLocal<>();
      
              IntStream.range(0, 10).forEach(i -> new Thread(() -> {
                  local.set(Thread.currentThread().getName() + ":" + i);
                  System.out.println("線程:" + Thread.currentThread().getName() + ",local:" + local.get());
              }).start());
          }
      
      }

       

      從結果可以看到,每一個線程都有自己的local 值,這就是TheadLocal的基本使用 。

      3. ThreadLocal工作原理

      下面我們從源碼的角度來分析一下,ThreadLocal的工作原理。

      3.1、set 方法

      /**
           * Sets the current thread's copy of this thread-local variable
           * to the specified value.  Most subclasses will have no need to
           * override this method, relying solely on the {@link #initialValue}
           * method to set the values of thread-locals.
           *
           * @param value the value to be stored in the current thread's copy of
           *        this thread-local.
           */
          public void set(T value) {
              //首先獲取當前線程對象
              Thread t = Thread.currentThread();
              //獲取線程中變量 ThreadLocal.ThreadLocalMap
              ThreadLocalMap map = getMap(t);
              //如果不為空,
              if (map != null)
                  map.set(this, value);
              else
                  //如果為空,初始化該線程對象的map變量,其中key 為當前的threadlocal 變量
                  createMap(t, value);
          }
      
          /**
           * Create the map associated with a ThreadLocal. Overridden in
           * InheritableThreadLocal.
           *
           * @param t the current thread
           * @param firstValue value for the initial entry of the map
           */
      //初始化線程內部變量 threadLocals ,key 為當前 threadlocal
          void createMap(Thread t, T firstValue) {
              t.threadLocals = new ThreadLocalMap(this, firstValue);
          }
      
             /**
               * Construct a new map initially containing (firstKey, firstValue).
               * ThreadLocalMaps are constructed lazily, so we only create
               * one when we have at least one entry to put in it.
               */
              ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
                  table = new Entry[INITIAL_CAPACITY];
                  int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
                  table[i] = new Entry(firstKey, firstValue);
                  size = 1;
                  setThreshold(INITIAL_CAPACITY);
              }
      
      
       static class Entry extends WeakReference<ThreadLocal<?>> {
                  /** The value associated with this ThreadLocal. */
                  Object value;
      
                  Entry(ThreadLocal<?> k, Object v) {
                      super(k);
                      value = v;
                  }
              }

      匯總下,ThreadLocalMap 為 ThreadLocal 的一個靜態內部類,里面定義了Entry 來保存數據。而且是繼承的弱引用。在Entry內部使用ThreadLocal作為key,使用我們設置的value作為value。

      對于每個線程內部有個ThreadLocal.ThreadLocalMap 變量,存取值的時候,也是從這個容器中來獲取。

      3.2、get方法

       

      /**
           * Returns the value in the current thread's copy of this
           * thread-local variable.  If the variable has no value for the
           * current thread, it is first initialized to the value returned
           * by an invocation of the {@link #initialValue} method.
           *
           * @return the current thread's value of this thread-local
           */
          public T get() {
              Thread t = Thread.currentThread();
              ThreadLocalMap map = getMap(t);
              if (map != null) {
                  ThreadLocalMap.Entry e = map.getEntry(this);
                  if (e != null) {
                      @SuppressWarnings("unchecked")
                      T result = (T)e.value;
                      return result;
                  }
              }
              return setInitialValue();
          }

      通過上面的分析,相信你對該方法已經有所理解了,首先獲取當前線程,然后通過key threadlocal 獲取 設置的value 。

      4. ThreadLocal 內存泄漏問題

      我們首先來看下,下面這個類:

      /**
               * The entries in this hash map extend WeakReference, using
               * its main ref field as the key (which is always a
               * ThreadLocal object).  Note that null keys (i.e. entry.get()
               * == null) mean that the key is no longer referenced, so the
               * entry can be expunged from table.  Such entries are referred to
               * as "stale entries" in the code that follows.
               */
              static class Entry extends WeakReference<ThreadLocal<?>> {
                  /** The value associated with this ThreadLocal. */
                  Object value;
      
                  Entry(ThreadLocal<?> k, Object v) {
                      super(k);
                      value = v;
                  }
              }

      注釋說的很清楚了,Note that null keys (i.e. entry.get()* == null)

      如果 key threadlocal 為 null 了,這個 entry 就可以清除了。

      ThreadLocal是一個弱引用,當為null時,會被當成垃圾回收 。

       

       

      重點來了,突然我們ThreadLocal是null了,也就是要被垃圾回收器回收了,但是此時我們的ThreadLocalMap(thread 的內部屬性)生命周期和Thread的一樣,它不會回收,這時候就出現了一個現象。那就是ThreadLocalMap的key沒了,但是value還在,這就造成了內存泄漏。

      解決辦法:使用完ThreadLocal后,執行remove操作,避免出現內存溢出情況。

      所以 如同 lock 的操作 最后要執行解鎖操作一樣,ThreadLocal使用完畢一定記得執行remove 方法,清除當前線程的數值。

      如果不remove 當前線程對應的VALUE ,就會一直存在這個值。

      使用了線程池,可以達到“線程復用”的效果。但是歸還線程之前記得清除ThreadLocalMap,要不然再取出該線程的時候,ThreadLocal變量還會存在。這就不僅僅是內存泄露的問題了,整個業務邏輯都可能會出錯。

       

      5. 為什么key使用弱引用?

      如果使用強引用,當ThreadLocal 對象的引用(強引用)被回收了,ThreadLocalMap本身依然還持有ThreadLocal的強引用,如果沒有手動刪除這個key ,則ThreadLocal不會被回收,所以只要當前線程不消亡,ThreadLocalMap引用的那些對象就不會被回收, 可以認為這導致Entry內存泄漏。

      附:強引用-軟引用-弱引用

      • 強引用:普通的引用,強引用指向的對象不會被回收;
      • 軟引用:僅有軟引用指向的對象,只有發生gc且內存不足,才會被回收;
      • 弱引用:僅有弱引用指向的對象,只要發生gc就會被回收。

       

       

      posted on 2023-03-04 19:15  陳國利  閱讀(99)  評論(0)    收藏  舉報

      主站蜘蛛池模板: 综合色在线| 欧美精品v国产精品v日韩精品 | 后入内射无码人妻一区| 国产国语毛片在线看国产| 风骚少妇久久精品在线观看| 国精品无码一区二区三区在线蜜臀 | 人妻少妇精品系列| 99久久国语露脸精品国产| 国产高清乱码又大又圆| 国产亚洲国产亚洲国产亚洲 | 国产成人精品一区二区三区| 国产乱码精品一区二区上| 九九热在线免费视频播放| 久久人人97超碰国产精品| 熟妇的奶头又大又长奶水视频| 午夜激情福利在线免费看| 中文字幕有码高清日韩| 国产成人精品亚洲午夜| 在线观看亚洲欧美日本| 蜜臀人妻精品一区二区免费| 亚洲国产欧美在线人成AAAA| 无遮高潮国产免费观看| 欧美人与动牲猛交A欧美精品| 久久久久久伊人高潮影院| 99久久久国产精品免费无卡顿| 亚洲国内精品一区二区| 九九热在线免费视频观看| 又黄又刺激又黄又舒服| 精品一区精品二区制服| 日本高清不卡一区二区三| 好紧好爽午夜视频| 亚洲国产成人精品无色码| 鲁一鲁一鲁一鲁一澡| 在线天堂www在线| 久久一日本道色综合久久| 67194熟妇在线观看线路| 国产成人精品电影在线观看| 亚洲综合av永久无码精品一区二区| 日韩精品中文字幕人妻| 安岳县| 77se77亚洲欧美在线|