精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

ThreadLocal基本內容與用法

科技綠洲 ? 來源:Java技術指北 ? 作者:Java技術指北 ? 2023-10-13 11:39 ? 次閱讀

下面我們就來看看道哥都用的ThreadLocal。

1 ThreadLocal你來自哪里

Since: 1.2
Author: Josh Bloch and Doug Lea

又是并發大佬們的杰作,膜拜一下。怪不得道哥也愛用,自己設計的類總得用用。下面來看看基本內容與用法吧。

2 ThreadLocal原理

“This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).”

“此類提供了thread-local變量。這些變量不同于普通的類似變量,因為訪問某個變量(通過其 get 或 set 方法)的每個線程都有自有的,獨立初始化的變量副本,ThreadLocal實例通常是希望將狀態與線程(例如,用戶ID或事務ID)關聯的類中的私有靜態字段。”

通過老爺子們的描述,指北君大概也知道了ThreadLocal的推薦使用場景,

  1. ThreadLocal提供了一種訪問某個特有變量的方法 訪問到的變量屬于當前線程,同一線程在任何地方都能訪問同一個線程特有變量。
  2. 推薦定義為 private static 類型,但是Doug Lea老爺子在ThreadLocalRandom 和 ReentrantReadWriteLock 中使用了 private static final 類型。(肯定是當年寫簡介的時候手抖了)

2.1 Thread中如何存儲

既然是線程的變量,自然是存在Thread對象中的一個變量了,但是它是通過ThreadLocal這個類來維護的。

//與此線程相關的ThreadLocal值,由ThreadLocal這個類維護
ThreadLocal.ThreadLocalMap threadLocals = null;

//與此線程相關的可繼承的ThreadLocal值,由InheritableThreadLocal類來維護
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

ThreadLocal中有一個內部類來ThreadLocalMap來維護這些線程本地變量,

static class ThreadLocalMap {
        //初始容量,2的n次方
        private static final int INITIAL_CAPACITY = 16; 
        
        //根據需要調整數組大小,2的n次方
        private Entry[] table;

        //上面Entry數組中的元素數量
        private int size = 0;

        //The next size value at which to resize  Default to 0
        private int threshold; 
}

ThreadLocalMap中的Entry結構如下,是一種key為弱引用(其目的就是Entry對象在GC時容易回收)的hash map,其中key總是ThreadLocal。

static class Entry extends WeakReference< ThreadLocal< ? >> {
    /** The value associated with this ThreadLocal. */
    Object value;
    Entry(ThreadLocal< ? > k, Object v) {
        super(k);
        value = v;
    }
}

2.2 常用方法 get,set,remove 詳解

  • get() 此方法是ThreadLocal最重要的方法之一,該方法返回此線程局部變量的當前線程副本中的值。大概可分為以下幾步:
    (1) 先獲取當前線程,然后再從線程中得到ThreadLocalMap。
    (2) 然后使用ThreadLocal對象的threadLocalHashCode進行散列計算,得到一個數組的index
    (3) 從Table數組中得到Entry,再對比Entry的key是不是和當前的ThreadLocal相等,如果相等就返回此Entry的value
    (4) 如果上一步中得到的Entry與當前ThreadLocal不相等,則會在方法getEntryAfterMiss中進行遍歷Entry數組table中的每一個元素,如果找不到就返回null。而且在遍歷的過程中會順便清理一下廢棄的Entry。

下面可以看一下get方法的具體代碼。

public T get() {
    //獲取當前線程
    Thread t = Thread.currentThread(); 

    //從當前線程中獲取ThreadLocalMap
    ThreadLocalMap map = getMap(t); 
    if (map != null) {
        
        //獲取map中當前ThreadLocal對象對應的entry
        ThreadLocalMap.Entry e = map.getEntry(this); 
        if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
        }
    }
    return setInitialValue();
}

private Entry getEntry(ThreadLocal< ? > key) {
    //散列計算得到Entry中當前的index
    int i = key.threadLocalHashCode & (table.length - 1);
    Entry e = table[i];
    
    //如果Entry不是null而且key等于當前
    // ThreadLocal對象則返回此Entry
    if (e != null && e.get() == key) 
        return e;
    else
        
        //Entry==null 或者其key不等于當前
        // ThreadLocal對象,遍歷其余Entry
        return getEntryAfterMiss(key, i, e);
}

private Entry getEntryAfterMiss(ThreadLocal< ? > key, int i, Entry e) {
    Entry[] tab = table;
    int len = tab.length;
    while (e != null) {
        ThreadLocal< ? > k = e.get();
        if (k == key) return e;
        if (k == null)
            //如果遍歷過程中發現有Entry的Key為Null,
            // 則清除掉作廢的Entry
            expungeStaleEntry(i);
        else
            //計算Entry數組下一個index
            i = nextIndex(i, len);
            e = tab[i];
        }
        return null;
}
  • set(T value) 此方法將此線程局部變量的當前線程副本中的值設置為指定值。

set線程本地變量步驟如下:
(1) 首先依然是獲取此線程的ThreadLocalMap
(2) Map不為null時往map中插入數據,否側創建map并插入數據
(3) 具體的set方法依然是先遍歷Entry數組中所有的的Entry,然后依次對比每個Entry的key是否等于當前ThreadLocal,如果相等則直接替換現有Entry的value。如果Entry的Key為null,則立馬清理廢棄的Entry,并用新的Entry來替換此卡槽。
(4) 如果遍歷完都沒有return,則在在table中相應卡槽下新建Entry對象

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 void set(ThreadLocal< ? > key, Object value) {
    Entry[] tab = table;
    int len = tab.length;
    int i = key.threadLocalHashCode & (len-1);
    for (Entry e = tab[i];
         e != null;
         e = tab[i = nextIndex(i, len)]) {
        ThreadLocal< ? > k = e.get();
        
        //如果原Entry的key就是當前ThreadLocal對象,
        // 則直接替換現有value
        if (k == key) {     
            e.value = value;
            return;
        }
        if (k == null) {
            
    // 如果Entry的Key為null, 則直接替換為新的Entry            
            replaceStaleEntry(key, value, i); 
            return;
        }
    }
    // 如果前面的遍歷沒有return,
    // 則插入新的Entry對象到對應的卡槽    
    tab[i] = new Entry(key, value); 
    int sz = ++size;
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
}
  • remove() remove則相對簡單,直接遍歷ThreadLocalMap中Entry數組table,找到對應的Entry,將Entry的key置為null,然后再清理相應的Entry。
private void remove(ThreadLocal< ? > key) {
    ...
    for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
        if (e.get() == key) {
            //Entry 的key置為null
            e.clear();
            // 清理對應卡槽,
            expungeStaleEntry(i); 
            return;
        }
    }
}

3 Java中使用的ThreadLocal

Java中有哪些源碼使用了ThreadLocal。

ThreadLocalRandom 中使用計算nextGaussian值時有使用到ThreadLocal。

InheritableThreadLocal繼承了ThreadLocal,線程中使用inheritableThreadLocals這個map存儲線程本地變量。和ThreadLocal的區別就是子線程依然可以訪問到父線程的線程本地變量,實際應用中也推薦InheritableThreadLocal

ReentrantReadWriteLock中線程讀寫鎖的計數器使用了ThreadLocal,其目的是記錄每個線程獲取讀寫鎖的次數

static final class ThreadLocalHoldCounter 
        extends ThreadLocal< HoldCounter > {
    public HoldCounter initialValue() {
        return new HoldCounter();
    }
}
//曾經的Doug Lea老爺子推薦static field,
// 而他默默的使用了static final。

4 如何使用ThreadLocal

ThreadLocal非常適合存儲非線程安全的對象,并且不需要跨線程共享對象。很多需要線程隔離的操作都可以嘗試使用它。

ThreadLocal也非常適合在Web應用程序中使用,典型的應用就是在Web請求進來一開始就將請求狀態存儲在ThreadLocal中,然后參與處理的任何組件均可訪問該狀態。

以下是一個ThreadLocal示例:

具體使用就是配合interceptor或者filter在線程剛開始執行的時候存儲SessionContext,線程執行過程中可以隨時訪問該變量。然后在線程執行結束的時候再調用remove()方法移除,防止內存泄漏。

public class SessionContextHolder {
    private static final ThreadLocal< SessionContex > CONTEXHOLDER 
                    = new InheritableThreadLocal<  >();
    
    public static void remove(){CONTEXHOLDER.remove();};
    
    public static SessionContex get(){return CONTEXHOLDER.get();}
    
    public static void set(SessionContex sessionContex) {CONTEXHOLDER.set(sessionContex);}
}

總結

本文介紹了ThreadLocal的原理以及解析了常用方法的實現邏輯,以及在ThreadLocal一些應用。在一步步梳理的過程中,果然看到了以往忽略的各種細節,最后給出了一個小Case。并發編程大神道哥.李都在用的ThreadLocal,不妨在自己的項目中偷偷用上,保證絲滑舒適。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 存儲
    +關注

    關注

    13

    文章

    4263

    瀏覽量

    85671
  • 數組
    +關注

    關注

    1

    文章

    415

    瀏覽量

    25908
  • 線程
    +關注

    關注

    0

    文章

    504

    瀏覽量

    19651
收藏 人收藏

    評論

    相關推薦

    電機選型的基本內容有哪些 電機選型需要哪些參數

    電機選型需要的基本內容有:所驅動的負載類型、額定功率、額定電壓、額定轉速、其它條件。電機的種類有很多,在選電機的時候選一臺好的電機非常重要,那么電機選型的詳細步驟有哪些呢?下面我們來介紹一下電機選型
    發表于 08-05 10:29 ?1.1w次閱讀

    ThreadLocal實例應用

    ThreadLocal相信大家都用過,但你知道他的原理嗎,今天了不起帶大家學習ThreadLocalThreadLocal是什么 在多線程編程中,經常會遇到需要在不同線程中共享數據的情況
    的頭像 發表于 09-30 10:19 ?639次閱讀
    <b class='flag-5'>ThreadLocal</b>實例應用

    ThreadLocal的定義、用法及優點

    ThreadLocal的定義、用法及其優點。 ThreadLocal是Java中一個用來實現線程封閉技術的類。它提供了一個本地線程變量,可以在多線程環境下使每個線程都擁有自己的變量副本。每個線程都可以獨立地改變自己
    的頭像 發表于 09-30 10:14 ?1002次閱讀
    <b class='flag-5'>ThreadLocal</b>的定義、<b class='flag-5'>用法</b>及優點

    USS通信協議的基本內容

    USS通信技術作為一種低成本的簡單驅動控制技術,在工業現場有著廣泛的應用。今天這篇文章,我們就和大家一起聊聊USS通信協議的基本內容
    發表于 01-19 06:45

    STM32單片機中需要用到的C語言知識有哪些

    STM32單片機中需要用到的C語言知識一、基本內容二、疑問點1.聲明變量2.預處理一、基本內容二、疑問點1.聲明變量const:可創建全局常量 局部常量, 數字常量, 數組常量 結構常量. 用法
    發表于 07-15 09:24

    課程實驗指導書格式,具體項目指導書格式與基本內容要求

    附:1.課程實驗指導書封面格式2.課程實驗指導書前言內容要求3.具體項目指導書格式與基本內容要求4.學生實驗報告基本內容要求具體項目指導書格式與基本內容
    發表于 09-24 11:39 ?0次下載

    《電視技術》課程基本內容及要求(電子專業)

    《電視技術》課程基本內容及要求(電子專業)本課程教學時數為48課時,教學內容及課程要求如下1、廣播電視的基本知識熟悉光電轉換過程;電視掃描原理;掌握廣播電
    發表于 03-12 09:31 ?17次下載

    電子線路CAA的基本內容及發展動態

    本文綜述了電子線路計算機輔助分析(CAA)的基本內容和方法;提出了這一領域目前正在研究的有關課題。關鍵詞:大規模網絡分析;非線性網絡分析;開關電容網絡分析;計
    發表于 06-01 13:31 ?15次下載

    基爾霍夫定律的基本內容是什么?

    基爾霍夫定律的基本內容是什么?基爾霍夫定律是電路分析和計算的重要工具。依據它可以作出決定電路特性的電流方程式和電壓方程式。基爾霍夫定律又分為第
    發表于 10-04 15:08 ?9450次閱讀
    基爾霍夫定律的<b class='flag-5'>基本內容</b>是什么?

    電子測量的基本內容

    電子測量的基本內容 隨著科學技術的不斷發展,測量的內容越來越多。電參數的測量分為電磁測量和電子測量兩類,前者著重研究交
    發表于 07-11 15:23 ?3035次閱讀

    液壓傳動系統設計的基本內容和一般流程

    本問介紹了液壓傳動系統的形式及禁忌、詳細的介紹了液壓傳動系統設計的基本內容和一般流程,最后介紹了液壓控制禁忌及分支管路的功率分配問題。
    發表于 01-04 13:56 ?8982次閱讀
    液壓傳動系統設計的<b class='flag-5'>基本內容</b>和一般流程

    AVR單片機教程之SPI的用法程序資料說明

    關于SPI的一些基本內容就不再在這說了,下面主要是一些實用的用法知識。SPI是全雙工通信,即可以單工通信,又可以全雙工通信。在單工通信和半雙工通信,比較簡單,就是主機(從機)發送數據,對方接受數據。
    發表于 10-23 18:57 ?7次下載
    AVR單片機教程之SPI的<b class='flag-5'>用法</b>程序資料說明

    ThreadLocal發生內存泄漏的原因

    前言 ThreadLocal 的作用是提供線程內的局部變量,這種變量在線程的生命周期內起作用,減少同一個線程內多個函數或者組件之間一些公共變量的傳遞的復雜度。但是如果濫用 ThreadLocal
    的頭像 發表于 05-05 16:23 ?3658次閱讀

    如何使用ThreadLocal來避免內存泄漏

    本次給大家介紹重要的工具ThreadLocal。講解內容如下,同時介紹什么場景下發生內存泄漏,如何復現內存泄漏,如何正確使用它來避免內存泄漏。 ThreadLocal是什么?有哪些用途
    的頭像 發表于 08-20 09:29 ?4208次閱讀
    如何使用<b class='flag-5'>ThreadLocal</b>來避免內存泄漏

    ThreadLocal源碼解析及實戰應用

    ThreadLocal 是一個關于創建線程局部變量的類。
    的頭像 發表于 01-29 14:53 ?450次閱讀