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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...