一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

ThreadLocal實(shí)例應(yīng)用

科技綠洲 ? 來(lái)源:Java技術(shù)指北 ? 作者:Java技術(shù)指北 ? 2023-09-30 10:19 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

ThreadLocal相信大家都用過(guò),但你知道他的原理嗎,今天了不起帶大家學(xué)習(xí)ThreadLocal。

ThreadLocal是什么

在多線程編程中,經(jīng)常會(huì)遇到需要在不同線程中共享數(shù)據(jù)的情況。通常情況下,為了保證線程安全,我們需要使用鎖或其他同步機(jī)制。然而,有些情況下,我們希望在每個(gè)線程中都有一份獨(dú)立的數(shù)據(jù)副本,這就是ThreadLocal派上用場(chǎng)的地方。

ThreadLocal翻譯過(guò)來(lái)就是線程本地,也就是本地線程變量,意思是ThreadLocal中填充的變量屬于當(dāng)前線程,該變量對(duì)其他線程而言是隔離的。ThreadLocal提供了一種機(jī)制,允許我們?yōu)槊總€(gè)線程創(chuàng)建獨(dú)立的變量,每個(gè)線程都可以獨(dú)立訪問(wèn)自己的變量,而不會(huì)干擾其他線程的數(shù)據(jù)。ThreadLocal為變量在每個(gè)線程中都創(chuàng)建了一個(gè)副本,那么每個(gè)線程可以訪問(wèn)自己內(nèi)部的副本變量,各個(gè)線程間互不影響,從而實(shí)現(xiàn)線程安全。

ThreadLocal的原理

ThreadLocal的原理涉及到兩個(gè)重要概念:ThreadLocal實(shí)例和ThreadLocalMap。

1. ThreadLocal實(shí)例

每個(gè)ThreadLocal對(duì)象實(shí)際上是一個(gè)容器,用于存儲(chǔ)線程本地的變量副本。每個(gè)線程都可以擁有自己的ThreadLocal實(shí)例,這些實(shí)例可以存儲(chǔ)不同的數(shù)據(jù),互相之間互不影響。

2. ThreadLocalMap

ThreadLocalMap是ThreadLocal的底層數(shù)據(jù)結(jié)構(gòu),它是一個(gè)哈希表。每個(gè)線程都有一個(gè)與之相關(guān)聯(lián)的ThreadLocalMap,用于存儲(chǔ)該線程所擁有的ThreadLocal實(shí)例以及對(duì)應(yīng)的值。ThreadLocalMap中的鍵是ThreadLocal實(shí)例,值是該線程對(duì)應(yīng)ThreadLocal實(shí)例的變量副本。

當(dāng)我們調(diào)用ThreadLocal的set()方法設(shè)置值時(shí),實(shí)際上是在當(dāng)前線程的ThreadLocalMap中以ThreadLocal實(shí)例為鍵,將值存儲(chǔ)在對(duì)應(yīng)的位置。而調(diào)用get()方法時(shí),則是從當(dāng)前線程的ThreadLocalMap中根據(jù)ThreadLocal實(shí)例獲取對(duì)應(yīng)的值。

ThreadLocal怎么用,應(yīng)用場(chǎng)景

public static void main(String[] args) {
   IntStream.range(1, 5).forEach(i - > new Thread(() - > {
       // 設(shè)置線程中本地變量的值
       threadLocal.set("thread-" + i);
       // 打印當(dāng)前線程中本地內(nèi)存中本地變量的值
       System.out.println(threadLocal.get());
       // 清除本地內(nèi)存中的本地變量
       threadLocal.remove();
       // 打印本地變量
       System.out.println("thread-" + i + " after remove: " + threadLocal.get());
   }).start());
}
/*
thread-1
thread-4
thread-4 after remove: null
thread-2
thread-3
thread-2 after remove: null
thread-1 after remove: null
thread-3 after remove: null
*/

從結(jié)果可以看到,每一個(gè)線程都有各自的值,并且互不影響。

應(yīng)用場(chǎng)景

  1. 用戶訪問(wèn)之后,在本地線程保存用戶的身份信息,在本次訪問(wèn)過(guò)程中,可以隨時(shí)獲取用戶的身份信息以驗(yàn)證身份。
  2. 在進(jìn)行對(duì)象跨層傳遞的時(shí)候,使用ThreadLocal可以避免多次傳遞,打破層次間的約束。
  3. 數(shù)據(jù)庫(kù)連接管理:每個(gè)線程可以擁有自己的數(shù)據(jù)庫(kù)連接,避免了線程之間的數(shù)據(jù)庫(kù)連接混淆。
  4. 用戶身份管理:在Web應(yīng)用中,可以將用戶身份信息存儲(chǔ)在ThreadLocal中,以便在整個(gè)請(qǐng)求處理過(guò)程中方便地訪問(wèn)。
  5. 事務(wù)管理:將事務(wù)狀態(tài)存儲(chǔ)在ThreadLocal中,確保每個(gè)線程都能獨(dú)立管理自己的事務(wù)狀態(tài)。

ThreadLocal源碼分析

先看一下 ThreadLocal 和 Thread 的關(guān)系

圖片

Thread類中有一個(gè)threadLocals屬性,是ThreadLocal內(nèi)部類ThreadLocalMap類型的變量,ThreadLocalMap可以看作是一個(gè)HashMap,其內(nèi)部有一個(gè)內(nèi)部類為 Entry,繼承了WeakReference>,是一個(gè)弱引用。Entry的key是ThreadLocal,value是Object類型的值。

大致了解了Thread和ThreadLocal的關(guān)系之后,看一下Thread Local的源碼:我們只要看其主要的幾個(gè)方法,就可以完全了解ThreadLocal的原理了。

set方法

public void set(T value) {
    // 獲取當(dāng)前線程
    Thread t = Thread.currentThread();
    // 通過(guò)當(dāng)前線程獲取線程中的ThreadLocal.ThreadLocalMap對(duì)象
    ThreadLocalMap map = getMap(t);
    if (map != null)
        // map不為空,則直接賦值
        map.set(this, value);
    else
        // map為空,則創(chuàng)建一個(gè)ThreadLocalMap對(duì)象
        createMap(t, value);
}
// 根據(jù)提供的線程對(duì)象,和指定的值,創(chuàng)建一個(gè)ThreadLocalMap對(duì)象
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}
// threadLocals是Thread類的一個(gè)屬性
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

/*
Thread 類 182行
 // ThreadLocal values pertaining to this thread. This map is maintained by the ThreadLocal class.
 與該線程有關(guān)的ThreadLocal值。這個(gè)映射由ThreadLocal類維護(hù)
    ThreadLocal.ThreadLocalMap threadLocals = null;
*/

get方法

// ThreadLocalMap中的內(nèi)部類,存放key,value
static class Entry extends WeakReference< ThreadLocal< ? >> {
    // 與此ThreadLocal關(guān)聯(lián)的值
    Object value;
 // k:ThreadLocal的引用,被傳遞給WeakReference的構(gòu)造方法
    Entry(ThreadLocal< ? > k, Object v) {
        super(k);
        value = v;
    }
}

public T get() {
    // 獲取當(dāng)前線程
    Thread t = Thread.currentThread();
    // 通過(guò)當(dāng)前線程獲取線程中的ThreadLocal.ThreadLocalMap對(duì)象
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        // map不為空,通過(guò)this(當(dāng)前對(duì)象,即ThreadLocal對(duì)象)獲取Entry對(duì)象
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            // Entry不為空,則直接返回Entry中的value值
            return result;
        }
    }
    // 如果map或Entry為空,則返回初始值-null
    return setInitialValue();
}
// 設(shè)置初始值,初始化ThreadLocalMap對(duì)象,并設(shè)置value為 null
private T setInitialValue() {
    // 初始化值,此方法返回 null
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

remove方法

public void remove() {
    // 通過(guò)當(dāng)前線程獲取線程中的ThreadLocal.ThreadLocalMap對(duì)象
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        // 移除對(duì)象
        m.remove(this);
}
// 根據(jù)key,刪除對(duì)應(yīng)的所有值
private void remove(ThreadLocal< ? > key) {
    Entry[] tab = table;
    int len = tab.length;
    // 獲取key對(duì)應(yīng)的 Entry[] 下標(biāo)
    int i = key.threadLocalHashCode & (len-1);
    for (Entry e = tab[i];
         e != null;
         // 獲取下一個(gè)Entry對(duì)象
         e = tab[i = nextIndex(i, len)]) {
        if (e.get() == key) {
            e.clear();
            // 通過(guò)重新哈希位于staleSlot和下一個(gè)null插槽之間的任何可能沖突的條目,來(lái)清除陳舊的條目。這還會(huì)清除尾隨null之前遇到的所有其他過(guò)時(shí)的條目,防止出現(xiàn)內(nèi)存泄漏問(wèn)題
            expungeStaleEntry(i);
            return;
        }
    }
}

總結(jié):

  1. 每個(gè)Thread維護(hù)著一個(gè)ThreadLocalMap的引用
  2. ThreadLocalMap是ThreadLocal的內(nèi)部類,用Entry來(lái)進(jìn)行存儲(chǔ)
  3. ThreadLocal創(chuàng)建的副本是存儲(chǔ)在自己的threadLocals中的,也就是自己的ThreadLocalMap。
  4. ThreadLocalMap的鍵為T(mén)hreadLocal對(duì)象,而且可以有多個(gè)threadLocal變量,因此保存在map中
  5. 在進(jìn)行g(shù)et之前,必須先set,否則會(huì)報(bào)空指針異常,當(dāng)然也可以初始化一個(gè),但是必須重寫(xiě)initialValue()方法。
  6. ThreadLocal本身并不存儲(chǔ)值,它只是作為一個(gè)key來(lái)讓線程從ThreadLocalMap獲取value。

注意事項(xiàng)

雖然ThreadLocal在某些情況下非常有用,但過(guò)度使用它也可能導(dǎo)致內(nèi)存泄漏問(wèn)題。因?yàn)門(mén)hreadLocalMap中的數(shù)據(jù)只有在線程結(jié)束時(shí)才會(huì)被釋放,如果沒(méi)有正確地清理ThreadLocal實(shí)例,就可能會(huì)導(dǎo)致無(wú)限制的數(shù)據(jù)積累。

另外,ThreadLocal不適合在并發(fā)量非常大的情況下使用,因?yàn)槊總€(gè)線程都會(huì)創(chuàng)建自己的變量副本,可能會(huì)導(dǎo)致內(nèi)存消耗過(guò)大。

ThreadLocal內(nèi)存泄漏問(wèn)題

在上面的代碼中,我們可以看出,當(dāng)前ThreadLocal的引用k被傳遞給WeakReference的構(gòu)造函數(shù),所以ThreadLocalMap中的key為T(mén)hreadLocal的弱引用。

如果當(dāng)前線程一直存在且沒(méi)有調(diào)用該ThreadLocal的remove方法,如果這個(gè)時(shí)候別的地方還有對(duì)ThreadLocal的引用,那么當(dāng)前線程中的ThreadLocalMap中會(huì)存在對(duì)ThreadLocal變量的引用和value對(duì)象的引用,是不會(huì)釋放的,會(huì)造成內(nèi)存泄漏。

ThreadLocalMap中的Entry的key使用的是ThreadLocal對(duì)象的弱引用,在沒(méi)有其他地方對(duì)ThreadLoca依賴,ThreadLocalMap中的ThreadLocal對(duì)象就會(huì)被回收掉,但是對(duì)應(yīng)的value值不會(huì)被回收,這個(gè)時(shí)候Map中就可能存在key為null但是value不為null的項(xiàng),也會(huì)造成內(nèi)存泄漏。

小結(jié)

ThreadLocal是一種多線程編程的工具,可以幫助我們?cè)诙嗑€程環(huán)境中管理線程本地的變量。它通過(guò)ThreadLocal實(shí)例和ThreadLocalMap的組合實(shí)現(xiàn)了這一功能。

使用ThreadLocal時(shí)需要注意內(nèi)存泄漏和性能問(wèn)題,確保合理使用。

使用完ThreadLocal后,一定執(zhí)行remove操作,避免出現(xiàn)內(nèi)存泄漏情況。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 數(shù)據(jù)
    +關(guān)注

    關(guān)注

    8

    文章

    7256

    瀏覽量

    91900
  • 內(nèi)存
    +關(guān)注

    關(guān)注

    8

    文章

    3125

    瀏覽量

    75275
  • 線程
    +關(guān)注

    關(guān)注

    0

    文章

    508

    瀏覽量

    20225
  • 多線程編程
    +關(guān)注

    關(guān)注

    0

    文章

    17

    瀏覽量

    6820
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    ThreadLocal的定義、用法及優(yōu)點(diǎn)

    ThreadLocal 簡(jiǎn)介 ThreadLocal是Java中一個(gè)非常重要的線程技術(shù)。它可以讓每個(gè)線程都擁有自己的變量副本,避免了線程間的競(jìng)爭(zhēng)和數(shù)據(jù)泄露問(wèn)題。在本文中,我們將詳細(xì)介紹
    的頭像 發(fā)表于 09-30 10:14 ?1429次閱讀
    <b class='flag-5'>ThreadLocal</b>的定義、用法及優(yōu)點(diǎn)

    傳感器應(yīng)用實(shí)例--數(shù)字“表頭”電路實(shí)例

    傳感器應(yīng)用實(shí)例--數(shù)字“表頭”電路實(shí)例
    發(fā)表于 12-11 23:15 ?2次下載

    傳感器應(yīng)用實(shí)例--冷端溫度補(bǔ)償電路實(shí)例

    傳感器應(yīng)用實(shí)例--冷端溫度補(bǔ)償電路實(shí)例
    發(fā)表于 12-11 23:03 ?4次下載

    傳感器應(yīng)用實(shí)例--光電轉(zhuǎn)換電路實(shí)例

    傳感器應(yīng)用實(shí)例--光電轉(zhuǎn)換電路實(shí)例
    發(fā)表于 12-11 23:03 ?57次下載

    單片機(jī)實(shí)例100入門(mén)實(shí)例知識(shí)

    單片機(jī)實(shí)例100入門(mén)實(shí)例知識(shí)
    發(fā)表于 09-21 08:32 ?38次下載
    單片機(jī)<b class='flag-5'>實(shí)例</b>100入門(mén)<b class='flag-5'>實(shí)例</b>知識(shí)

    ThreadLocal發(fā)生內(nèi)存泄漏的原因

    ThreadLocal 實(shí)現(xiàn)原理 ThreadLocal的實(shí)現(xiàn)是這樣的:每個(gè)Thread 維護(hù)一個(gè) ThreadLocalMap 映射表,這個(gè)映射表的 key 是 ThreadLocal 實(shí)
    的頭像 發(fā)表于 05-05 16:23 ?3919次閱讀

    如何使用ThreadLocal來(lái)避免內(nèi)存泄漏

    本次給大家介紹重要的工具ThreadLocal。講解內(nèi)容如下,同時(shí)介紹什么場(chǎng)景下發(fā)生內(nèi)存泄漏,如何復(fù)現(xiàn)內(nèi)存泄漏,如何正確使用它來(lái)避免內(nèi)存泄漏。 ThreadLocal是什么?有哪些用途
    的頭像 發(fā)表于 08-20 09:29 ?4473次閱讀
    如何使用<b class='flag-5'>ThreadLocal</b>來(lái)避免內(nèi)存泄漏

    FastThreadLocal快在哪里

    ThreadLocalMap實(shí)例變量(如果不使用ThreadLocal,不會(huì)創(chuàng)建這個(gè)Map,一個(gè)線程第一次訪問(wèn)某個(gè)ThreadLocal變量時(shí),才會(huì)創(chuàng)建)。 該Map是使用線性探測(cè)的方式解決hash沖突的問(wèn)題,如果沒(méi)有找到空閑的
    的頭像 發(fā)表于 09-13 09:17 ?1488次閱讀

    Device Studio應(yīng)用實(shí)例之LAMMPS應(yīng)用實(shí)例

    上一期的教程給大家介紹了Device Studio應(yīng)用實(shí)例之Nanodcal應(yīng)用實(shí)例的內(nèi)容,本期將介紹Device Studio應(yīng)用實(shí)例之LAMMPS應(yīng)用實(shí)例的內(nèi)容。
    的頭像 發(fā)表于 07-21 11:23 ?4287次閱讀

    Device Studio應(yīng)用實(shí)例之STEMS應(yīng)用實(shí)例

    上一期的教程給大家介紹了Device Studio應(yīng)用實(shí)例之STEMS應(yīng)用實(shí)例上半部分的內(nèi)容,本期將介紹Device Studio應(yīng)用實(shí)例之STEMS應(yīng)用實(shí)例下半部分的內(nèi)容。
    的頭像 發(fā)表于 07-30 11:06 ?2554次閱讀

    ThreadLocal的作用以及應(yīng)用場(chǎng)景

    舉一個(gè)簡(jiǎn)單的例子:目前有100個(gè)學(xué)生等待簽字,但是老師只有一個(gè)筆,那老師只能按順序的分給每個(gè)學(xué)生,等待A學(xué)生簽字完成然后將筆交給B學(xué)生,這就類似Lock,Synchronized的方式。而ThreadLocal是,老師直接拿出一百個(gè)筆給每個(gè)學(xué)生;再效率提高的同事也要付出一個(gè)內(nèi)存消耗;也就是以空間換時(shí)間的概念
    的頭像 發(fā)表于 09-19 10:56 ?1648次閱讀

    ThreadLocal源碼解析及實(shí)戰(zhàn)應(yīng)用

    ThreadLocal 是一個(gè)關(guān)于創(chuàng)建線程局部變量的類。
    的頭像 發(fā)表于 01-29 14:53 ?654次閱讀

    ThreadLocal是什么

    和HashMap的最大的不同在于,ThreadLocalMap結(jié)構(gòu)非常簡(jiǎn)單,沒(méi)有next引用,也就是說(shuō)ThreadLocalMap中解決Hash沖突的方式并非鏈表的方式,而是采用線性探測(cè)的方式。
    的頭像 發(fā)表于 01-30 11:36 ?1745次閱讀

    ThreadLocal父子線程之間該如何傳遞數(shù)據(jù)?

    比如會(huì)有以下的這種代碼的實(shí)現(xiàn)。在子線程中調(diào)用 get 時(shí),我們拿到的 Thread 對(duì)象是當(dāng)前子線程對(duì)象,對(duì)吧,每個(gè)線程都有自己獨(dú)立的 ThreadLocal,那么當(dāng)前子線程
    的頭像 發(fā)表于 02-20 11:26 ?1186次閱讀

    ThreadLocal基本內(nèi)容與用法

    下面我們就來(lái)看看道哥都用的ThreadLocal。 1 ThreadLocal你來(lái)自哪里 Since : 1.2 Author : Josh Bloch and Doug Lea 又是并發(fā)大佬們
    的頭像 發(fā)表于 10-13 11:39 ?698次閱讀