在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(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ú)立訪問自己的變量,而不會(huì)干擾其他線程的數(shù)據(jù)。ThreadLocal為變量在每個(gè)線程中都創(chuàng)建了一個(gè)副本,那么每個(gè)線程可以訪問自己內(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. 用戶訪問之后,在本地線程保存用戶的身份信息,在本次訪問過(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ò)程中方便地訪問。
  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)存泄漏問題
            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的鍵為ThreadLocal對(duì)象,而且可以有多個(gè)threadLocal變量,因此保存在map中
  5. 在進(jìn)行g(shù)et之前,必須先set,否則會(huì)報(bào)空指針異常,當(dāng)然也可以初始化一個(gè),但是必須重寫initialValue()方法。
  6. ThreadLocal本身并不存儲(chǔ)值,它只是作為一個(gè)key來(lái)讓線程從ThreadLocalMap獲取value。

注意事項(xiàng)

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

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

如果當(dāng)前線程一直存在且沒有調(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ì)象的弱引用,在沒有其他地方對(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)存泄漏和性能問題,確保合理使用。

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

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

    關(guān)注

    8

    文章

    7247

    瀏覽量

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

    關(guān)注

    8

    文章

    3115

    瀏覽量

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

    關(guān)注

    0

    文章

    507

    瀏覽量

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

    關(guān)注

    0

    文章

    17

    瀏覽量

    6788
收藏 人收藏

    評(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ù)泄露問題。在本文中,我們將詳細(xì)介紹
    的頭像 發(fā)表于 09-30 10:14 ?1386次閱讀
    <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í)例--光電轉(zhuǎn)換電路實(shí)例

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

    傳感器應(yīng)用實(shí)例--壓力測(cè)量電路實(shí)例

    傳感器應(yīng)用實(shí)例--壓力測(cè)量電路實(shí)例
    發(fā)表于 12-11 23:03 ?5次下載

    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 ?3888次閱讀

    如何使用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 ?4435次閱讀
    如何使用<b class='flag-5'>ThreadLocal</b>來(lái)避免內(nèi)存泄漏

    FastThreadLocal快在哪里

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

    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 ?4195次閱讀

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

    上一期的教程給大家介紹了Device Studio亮點(diǎn)功能7.7-7.9的內(nèi)容,本期將介紹Device Studio應(yīng)用實(shí)例之Nanodcal應(yīng)用實(shí)例的內(nèi)容。
    的頭像 發(fā)表于 07-26 15:32 ?2076次閱讀

    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 ?2483次閱讀

    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 ?1617次閱讀

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

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

    ThreadLocal是什么

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

    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 ?1151次閱讀

    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 ?669次閱讀
    主站蜘蛛池模板: 在线播放一区二区精品产 | 天天摸天天| 精品一区二区三区在线视频 | 国产人人艹 | 天天槽任我槽免费 | 特级一级黄色片 | 黑人xxxx精品 | 成人黄色免费 | 最新天堂 | 超级乱淫视频播放日韩 | 欧美xxxxxxxxx | 中文在线最新版天堂bt | 欧美色影视 | 免费看又爽又黄禁片视频1000 | 黄色永久网站 | 国产精品免费久久 | 李老汉和小花的性生生活 | 中国胖女人一级毛片aaaaa | www.你懂的.com| 视频二区中文字幕 | 一级色视频 | 美剧免费在线观看 | 亚洲精品久久久久午夜福 | а中文在线天堂 | 视频h在线 | 国产性较精品视频免费 | 色综合小说天天综合网 | 午夜手机福利视频 | 日本三级带日本三级带黄首页 | 美女黄18以下禁止观看的网站 | 国产三级精品播放 | 亚洲天堂视频一区 | 四虎最新永久在线精品免费 | 国产福利萌白酱喷水视频铁牛 | 午夜精品在线视频 | 又长又大又粗又硬3p免费视频 | 奇米影视一区二区三区 | 久久全国免费久久青青小草 | 久久天天躁狠狠躁夜夜免费观看 | 99热这里只有精品一区二区三区 | 久99热 |