1. 程式人生 > >ThreadLocal原理分析與使用場景

ThreadLocal原理分析與使用場景

什麼是ThreadLocal變數

ThreadLoal 變數,執行緒區域性變數,同一個 ThreadLocal 所包含的物件,在不同的 Thread 中有不同的副本。這裡有幾點需要注意:

  • 因為每個 Thread 內有自己的例項副本,且該副本只能由當前 Thread 使用。這是也是 ThreadLocal 命名的由來。
  • 既然每個 Thread 有自己的例項副本,且其它 Thread 不可訪問,那就不存在多執行緒間共享的問題。

ThreadLocal 提供了執行緒本地的例項。它與普通變數的區別在於,每個使用該變數的執行緒都會初始化一個完全獨立的例項副本。ThreadLocal 變數通常被private static修飾。當一個執行緒結束時,它所使用的所有 ThreadLocal 相對的例項副本都可被回收。

總的來說,ThreadLocal 適用於每個執行緒需要自己獨立的例項且該例項需要在多個方法中被使用,也即變數線上程間隔離而在方法或類間共享的場景。

ThreadLocal實現原理

首先 ThreadLocal 是一個泛型類,保證可以接受任何型別的物件。

因為一個執行緒內可以存在多個 ThreadLocal 物件,所以其實是 ThreadLocal 內部維護了一個 Map ,這個 Map 不是直接使用的 HashMap ,而是 ThreadLocal 實現的一個叫做 ThreadLocalMap 的靜態內部類。而我們使用的 get()、set() 方法其實都是呼叫了這個ThreadLocalMap類對應的 get()、set() 方法。例如下面的 set 方法:

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

get方法:

    public T get() {   
        Thread t = Thread.currentThread();   
        ThreadLocalMap map 
= getMap(t); if (map != null) return (T)map.get(this); // Maps are constructed lazily. if the map for this thread // doesn't exist, create it, with this ThreadLocal and its // initial value as its only entry. T value = initialValue(); createMap(t, value); return value; }

createMap方法:

    void createMap(Thread t, T firstValue) {   
        t.threadLocals = new ThreadLocalMap(this, firstValue);   
    } 

ThreadLocalMap是個靜態的內部類:

    static class ThreadLocalMap {   
    ........   
    }  

最終的變數是放在了當前執行緒的 ThreadLocalMap 中,並不是存在 ThreadLocal 上,ThreadLocal 可以理解為只是ThreadLocalMap的封裝,傳遞了變數值。

記憶體洩漏問題

實際上 ThreadLocalMap 中使用的 key 為 ThreadLocal 的弱引用,弱引用的特點是,如果這個物件只存在弱引用,那麼在下一次垃圾回收的時候必然會被清理掉。

所以如果 ThreadLocal 沒有被外部強引用的情況下,在垃圾回收的時候會被清理掉的,這樣一來 ThreadLocalMap中使用這個 ThreadLocal 的 key 也會被清理掉。但是,value 是強引用,不會被清理,這樣一來就會出現 key 為 null 的 value。

ThreadLocalMap實現中已經考慮了這種情況,在呼叫 set()、get()、remove() 方法的時候,會清理掉 key 為 null 的記錄。如果說會出現記憶體洩漏,那只有在出現了 key 為 null 的記錄後,沒有手動呼叫 remove() 方法,並且之後也不再呼叫 get()、set()、remove() 方法的情況下。

使用場景

如上文所述,ThreadLocal 適用於如下兩種場景

  • 每個執行緒需要有自己單獨的例項
  • 例項需要在多個方法中共享,但不希望被多執行緒共享

對於第一點,每個執行緒擁有自己例項,實現它的方式很多。例如可以線上程內部構建一個單獨的例項。ThreadLoca 可以以非常方便的形式滿足該需求。

對於第二點,可以在滿足第一點(每個執行緒有自己的例項)的條件下,通過方法間引用傳遞的形式實現。ThreadLocal 使得程式碼耦合度更低,且實現更優雅。

1)儲存使用者Session

一個簡單的用ThreadLocal來儲存Session的例子:

    private static final ThreadLocal threadSession = new ThreadLocal();

    public static Session getSession() throws InfrastructureException {
        Session s = (Session) threadSession.get();
        try {
            if (s == null) {
                s = getSessionFactory().openSession();
                threadSession.set(s);
            }
        } catch (HibernateException ex) {
            throw new InfrastructureException(ex);
        }
        return s;
    }

2)解決執行緒安全的問題

比如Java7中的SimpleDateFormat不是執行緒安全的,可以用ThreadLocal來解決這個問題:

public class DateUtil {
    private static ThreadLocal<SimpleDateFormat> format1 = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };

    public static String formatDate(Date date) {
        return format1.get().format(date);
    }
}

這裡的DateUtil.formatDate()就是執行緒安全的了。(Java8裡的 是執行緒安全的,Joda time裡的DateTimeFormat也是執行緒安全的)。

參考:

http://www.jasongj.com/java/threadlocal/

http://java.jiderhamn.se/2012/01/29/classloader-leaks-iv-threadlocal-dangers-and-why-threadglobal-may-have-been-a-more-appropriate-name/

相關推薦

ThreadLocal原理分析使用場景

什麼是ThreadLocal變數 ThreadLoal 變數,執行緒區域性變數,同一個 ThreadLocal 所包含的物件,在不同的 Thread 中有不同的副本。這裡有幾點需要注意: 因為每個 Thread 內有自己的例項副本,且該副本只能由當前 Thread 使用。這是也是 ThreadLoca

JDK基礎--ThreadLocal原理分析使用場景

開發十年,就只剩下這套架構體系了! >>>   

Netty學習之旅----ThreadLocal原理分析效能優化思考(思考篇)

/** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the

ThreadLocal原理分析程式碼驗證

ThreadLocal提供了執行緒安全的資料儲存和訪問方式,利用不帶key的get和set方法,居然能做到執行緒之間隔離,非常神奇。 比如 ThreadLocal<String> threadLocal = new ThreadLocal<>(); in thread 1 //in t

XSS的原理分析解剖:第三章(技巧篇)**************未看*****************

第二章 != chrom 插入 是把 調用 bject innerhtml ats ??0×01 前言: 關於前兩節url: 第一章:http://www.freebuf.com/articles/web/40520.html 第二章:http://www.free

Apriori 關聯分析算法原理分析代碼實現

muc items blog 具體實現 itblog run 任務 name subset 轉自穆晨 閱讀目錄 前言 關聯分析領域的一些概念 Apriori算法基本原理 頻繁項集檢索實現思路與實現代碼 關聯規則學習實現思路與實現代碼 小結 回到頂部 前言

K-Means 聚類算法原理分析代碼實現

oat 得到 ssi targe fan readline txt __name__ 輸出 轉自穆晨 閱讀目錄 前言 現實中的聚類分析問題 - 總統大選 K-Means 聚類算法 K-Means性能優化 二分K-Means算法 小結 回到頂部 前言 在

ThreadLocal原理、使用場景及存在記憶體洩漏的原因

什麼是執行緒封閉 當多執行緒訪問共享變數時,往往需要加鎖來保證共享變數的執行緒安全(資料同步)。一種避免使用加鎖方式就是不共享資料,而是讓執行緒獨享資料。由於資料本身就是執行緒私有的,這樣,如果僅在單執行緒內訪問資料就不需要同步,這種避免共享資料的技術稱為執行緒封閉。在Java語言中,提供了一些

tomcat原理分析簡單實現

一、思路概述 1.tomcat實際是執行在jvm中的一個程序。我們把它定義為【中介軟體】,顧名思義,他是一個在java專案與jvm之間的 中間容器。我們的web專案沒有入口方法(main方法),那麼他是如何執行起來併為客戶端返回資料的呢? 2.web專案[就javaee而講]

機器學習系列文章:Apriori關聯規則分析演算法原理分析程式碼實現

1.關聯規則淺談     關聯規則(Association Rules)是反映一個事物與其他事物之間的相互依存性和關聯性,如果兩個或多個事物之間存在一定的關聯關係,那麼,其中一個事物就能通過其他事物預測到。關聯規則是資料探勘的一個重要技術,用於從大量資料中挖掘出有價值的資料

Linux記憶體洩露之kmemleak原理分析使用

    1. kmemleak原理:  通過分析記憶體塊是否存在引用(指標)來判斷記憶體洩露. 1.1  掃描區域   首先要理解整個核心虛擬地址空間是怎麼分佈的, 核心地址空間分佈: Virtual kernel memory layout: vma

Presto UDF原理分析淺實踐

背景 這兩天在研究Presto的UDF,發現Presto的UDF的方式很特別,後來自己思考了一下實現方式(沒有看原始碼,有可能與Presto實現不同),現在把思路記錄一下。 思路 Presto UDF 最後是將生成的Jar放到到plugin中,並且呼叫實現com

歸併排序演算法原理分析程式碼實現

  歸併排序是建立在歸併操作上的一種有效的排序演算法。該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用,歸併排序將兩個已排序的表合併成一個表。 歸併排序基本原理

深入理解Java執行緒池原理分析使用(尤其當執行緒佇列滿了之後事項)

在這裡借花獻佛了,那別人的東西學一學了。在我們的開發中“池”的概念並不罕見,有資料庫連線池、執行緒池、物件池、常量池等等。下面我們主要針對執行緒池來一步一步揭開執行緒池的面紗。使用執行緒池的好處1、降低資源消耗可以重複利用已建立的執行緒降低執行緒建立和銷燬造成的消耗。2、提高響應速度當任務到達時,任務可以不需

Nginx出現Access Denied的原理分析解決方案

如果你發現Nginx伺服器出現Access Denied我覺得90%的可能性是Nginx配置檔案配置的有些小毛病,網上有些解決方案是修改php-fpm的配置檔案中的security.limit_extensions,在這個引數中增加訪問的副檔名,例如css、js等檔案出現Access Den

K-means聚類演算法原理分析實際應用案例分析(案例分析另起一篇部落格)

引言 在資料分析中,我們常常想將看上去相似或者行為形似的資料聚合在一起。例如,對一個營銷組織來說,將不同客戶根據他們的特點進行分組,從而有針對性地定製營銷活動,這很重要。又比如,對學校老師來說,將學生分組同樣能夠有所側重的進行教育活動。分類與聚類是資料探勘領域

SpringBoot學習筆記一之【Idea下建立springboot示例、啟動原理分析兩種部署啟動方式】

1、使用背景 首先說下我們為什麼使用springboot,原因有以下幾點 1、快速建立獨立執行的spring專案以及與主流框架繼承 2、使用嵌入式的Servlet容器,無需打成war包 3、starters自動依賴於版本控制 4、大量的自動配置,簡化開發,也可修改預設值 5、

第五篇:樸素貝葉斯分類演算法原理分析程式碼實現

1 #==================================== 2 # 輸入: 3 # 空 4 # 輸出: 5 # postingList: 文件列表 6 # classVec: 分類標籤列表 7 #=

webpack-dev-server原理分析HMR實現

建議在github閱讀,我會保證內容及時更新,並歡迎star,issue。如果你想深入瞭解webpack-dev-server的內部原理,你也可以檢視我寫的這個打包工具,通過它可以完成三種打包方式,其中devServer模式就是通過webpack-dev-ser

Spark Streaming工作原理分析使用

Spark Streaming入門1. 概述Spark Streaming 是 Spark Core API 的擴充套件, 它支援彈性的, 高吞吐的, 容錯的實時資料流的處理。spark streaming提供是一種分散式計算能力。資料來源資料可以通過多種資料來源獲取, 例如