ThreadLocal類

来源:http://www.cnblogs.com/clovejava/archive/2017/11/14/7835242.html
-Advertisement-
Play Games

ThreadLocal的主要應用場景為按線程多實例(每個線程對應一個實例)的對象的訪問,並且這個對象很多地方都要用到。例如:同一個網站登錄用戶,每個用戶伺服器會為其開一個線程,每個線程中創建一個ThreadLocal,裡面存用戶基本信息等,在很多頁面跳轉時,會顯示用戶信息或者得到用戶的一些信息等頻繁 ...


ThreadLocal的主要應用場景為按線程多實例(每個線程對應一個實例)的對象的訪問,並且這個對象很多地方都要用到。例如:同一個網站登錄用戶,每個用戶伺服器會為其開一個線程,每個線程中創建一個ThreadLocal,裡面存用戶基本信息等,在很多頁面跳轉時,會顯示用戶信息或者得到用戶的一些信息等頻繁操作,這樣多線程之間並沒有聯繫而且當前線程也可以及時獲取想要的數據。

一、實現原理

ThreadLocal可以看做是一個容器,容器裡面存放著屬於當前線程的變數。ThreadLocal類提供了四個對外開放的介面方法,這也是用戶操作ThreadLocal類的基本方法: 
(1) void set(Object value)設置當前線程的線程局部變數的值。 
(2) public Object get()該方法返回當前線程所對應的線程局部變數。 
(3) public void remove()將當前線程局部變數的值刪除,目的是為了減少記憶體的占用,該方法是JDK 5.0新增的方法。需要指出的是,當線程結束後,對應該線程的局部變數將自動被垃圾回收,所以顯式調用該方法清除線程的局部變數並不是必須的操作,但它可以加快記憶體回收的速度。 
(4) protected Object initialValue()返回該線程局部變數的初始值,該方法是一個protected的方法,顯然是為了讓子類覆蓋而設計的。這個方法是一個延遲調用方法,線上程第1次調用get()或set(Object)時才執行,並且僅執行1次,ThreadLocal中的預設實現直接返回一個null。

可以通過上述的幾個方法實現ThreadLocal中變數的訪問,數據設置,初始化以及刪除局部變數,那ThreadLocal內部是如何為每一個線程維護變數副本的呢?

其實在ThreadLocal類中有一個靜態內部類ThreadLocalMap(其類似於Map),用鍵值對的形式存儲每一個線程的變數副本,ThreadLocalMap中元素的key為當前ThreadLocal對象,而value對應線程的變數副本,每個線程可能存在多個ThreadLocal。

源代碼:

/**
 Returns the value in the current thread's copy of this
 thread-local variable.  If the variable has no value for thecurrent 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);//獲取當前線程對應的ThreadLocalMap
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);//獲取對應ThreadLocal的變數值
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();//若當前線程還未創建ThreadLocalMap,則返回調用此方法併在其中調用createMap方法進行創建並返回初始值。
}
//設置變數的值
public void set(T value) {
   Thread t = Thread.currentThread();
   ThreadLocalMap map = getMap(t);
   if (map != null)
       map.set(this, value);
   else
       createMap(t, value);
}
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);
   return value;
}
/**
為當前線程創建一個ThreadLocalMap的threadlocals,並將第一個值存入到當前map中
@param t the current thread
@param firstValue value for the initial entry of the map
*/
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}
//刪除當前線程中ThreadLocalMap對應的ThreadLocal
public void remove() {
       ThreadLocalMap m = getMap(Thread.currentThread());
       if (m != null)
           m.remove(this);
}

 

上述是在ThreadLocal類中的幾個主要的方法,他們的核心都是對其內部類ThreadLocalMap進行操作,下麵看一下該類的源代碼:

static class ThreadLocalMap {
  //map中的每個節點Entry,其鍵key是ThreadLocal並且還是弱引用,這也導致了後續會產生記憶體泄漏問題的原因。
 static class Entry extends WeakReference<ThreadLocal<?>> {
           Object value;
           Entry(ThreadLocal<?> k, Object v) {
               super(k);
               value = v;
   }
    /**
     * 初始化容量為16,以為對其擴充也必須是2的指數 
     */
    private static final int INITIAL_CAPACITY = 16;
    /**
     * 真正用於存儲線程的每個ThreadLocal的數組,將ThreadLocal和其對應的值包裝為一個Entry。
     */
    private Entry[] table;


    ///....其他的方法和操作都和map的類似
}

 

總之,為不同線程創建不同的ThreadLocalMap,用線程本身為區分點,每個線程之間其實沒有任何的聯繫,說是說存放了變數的副本,其實可以理解為為每個線程單獨new了一個對象。

二、記憶體泄漏問題

  在上面提到過,每個thread中都存在一個map, map的類型是ThreadLocal.ThreadLocalMap. Map中的key為一個threadlocal實例. 這個Map的確使用了弱引用,不過弱引用只是針對key. 每個key都弱引用指向threadlocal. 當把threadlocal實例置為null以後,沒有任何強引用指向threadlocal實例,所以threadlocal將會被gc回收. 但是,我們的value卻不能回收,因為存在一條從current thread連接過來的強引用. 只有當前thread結束以後, current thread就不會存在棧中,強引用斷開, Current Thread, Map, value將全部被GC回收. 
  所以得出一個結論就是只要這個線程對象被gc回收,就不會出現記憶體泄露,但在threadLocal設為null和線程結束這段時間不會被回收的,就發生了我們認為的記憶體泄露。其實這是一個對概念理解的不一致,也沒什麼好爭論的。最要命的是線程對象不被回收的情況,這就發生了真正意義上的記憶體泄露。比如使用線程池的時候,線程結束是不會銷毀的,會再次使用的。就可能出現記憶體泄露。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1. 什麼是Spring Spring是一個輕量級的容器,他實現了IOC和非侵入的框架,並提供了AOP的實現方式,提供了持久層事務的支持,其讓java開發模塊化,並且貫穿持久層,邏輯層,表現層,讓每一個模塊都可以獨立分開,降低耦合,提高代碼復用率. 2. Spring的好處 Spring提供了IOC ...
  • 從上到下 #grad { background: -webkit-linear-gradient(red, blue); background: -o-linear-gradient(red, blue); background: -moz-linear-gradient(red, blue); b ...
  • 1. struts2的工作原理 客戶端發送請求 經過一系列的過濾器 FilterDispatcher通過ActionMapper來決定這個REquest需要調用的Action FilterDispather交給ActionProxy 通過ConfigurationManager詢問struts.xm ...
  • 常用命令和使用方法如下: man cat 和 tac cat是正序顯示文件內容 tac是倒敘顯示文件內容 sort 對文件內容排序 uniq 忽略文件中重覆行 history 顯示輸入的歷史命令,一般保存兩千行命令 more more命令,功能類似 cat ,cat命令是整個文件的內容從上到下顯示在 ...
  • TensorFlow Serving https://tensorflow.github.io/serving/ 。 生產環境靈活、高性能機器學習模型服務系統。適合基於實際數據大規模運行,產生多個模型訓練過程。可用於開發環境、生產環境。 模型生命周期管理。模型先數據訓練,逐步產生初步模型,優化模型。 ...
  • 說說最近在開發微信小程式語音識別遇到的問題吧 最先使用微信小程式錄音控制項可以拿到silk格式,後來微信官方又支持mp3格式了 但是我們拿到這些格式以後,都還不能直接使用,做語音識別,因為目前百度的語音識別格式不支持mp3格式的 百度php語音識別介面 http://yuyin.baidu.com/d ...
  • 接上篇隨筆。繼續介紹ajax的使用。 上篇友情連接:http://www.cnblogs.com/liluning/p/7831169.html 本篇導航: Ajax響應參數 csrf 跨站請求偽造 jQuery.serialize() 上傳文件 一、Ajax響應參數 上篇最後介紹了ajax的請求參 ...
  • 1. Spring MVC執行過程 1. 客戶端的請求提交到dispatcherServlet 2. DispatcherServlet查詢一個或者多個handlermapping ,找請求的Controller 3. DispatcherServlet將請求提交給Controller, Contr ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...