最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java面試必問(wèn)之ThreadLocal終極篇分享

 更新時(shí)間:2021年10月08日 12:03:21   作者:占小狼  
ThreadLocal是什么呢?其實(shí)ThreadLocal并非是一個(gè)線程的本地實(shí)現(xiàn)版本,它并不是一個(gè)Thread,而是thread local variable(線程局部變量),這篇文章主要給大家介紹了關(guān)于Java面試必問(wèn)之ThreadLocal終極篇的相關(guān)資料,需要的朋友可以參考下

前言

在面試環(huán)節(jié)中,考察"ThreadLocal"也是面試官的家常便飯,所以對(duì)它理解透徹,是非常有必要的.
有些面試官會(huì)開門見山的提問(wèn):

  • “知道ThreadLocal嗎?”
  • “講講你對(duì)ThreadLocal的理解”

當(dāng)然了,也有面試官會(huì)慢慢引導(dǎo)到這個(gè)話題上,比如提問(wèn)“在多線程環(huán)境下,如何防止自己的變量被其它線程篡改”,將主動(dòng)權(quán)交給你自己,剩下的靠自己發(fā)揮。

那么ThreadLocal可以做什么,在了解它的應(yīng)用場(chǎng)景之前,我們先看看它的實(shí)現(xiàn)原理,只有知道了實(shí)現(xiàn)原理,才好判斷它是否符合自己的業(yè)務(wù)場(chǎng)景。

ThreadLocal是什么

首先,它是一個(gè)數(shù)據(jù)結(jié)構(gòu),有點(diǎn)像HashMap,可以保存"key : value"鍵值對(duì),但是一個(gè)ThreadLocal只能保存一個(gè),并且各個(gè)線程的數(shù)據(jù)互不干擾。

ThreadLocal<String> localName = new ThreadLocal();
localName.set("占小狼");
String name = localName.get();

在線程1中初始化了一個(gè)ThreadLocal對(duì)象localName,并通過(guò)set方法,保存了一個(gè)值占小狼,同時(shí)在線程1中通過(guò)localName.get()可以拿到之前設(shè)置的值,但是如果在線程2中,拿到的將是一個(gè)null。

這是為什么,如何實(shí)現(xiàn)?不過(guò)之前也說(shuō)了,ThreadLocal保證了各個(gè)線程的數(shù)據(jù)互不干擾。

看看set(T value)和get()方法的源碼

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

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

可以發(fā)現(xiàn),每個(gè)線程中都有一個(gè)ThreadLocalMap數(shù)據(jù)結(jié)構(gòu),當(dāng)執(zhí)行set方法時(shí),其值是保存在當(dāng)前線程的threadLocals變量中,當(dāng)執(zhí)行set方法中,是從當(dāng)前線程的threadLocals變量獲取。

所以在線程1中set的值,對(duì)線程2來(lái)說(shuō)是摸不到的,而且在線程2中重新set的話,也不會(huì)影響到線程1中的值,保證了線程之間不會(huì)相互干擾。

那每個(gè)線程中的ThreadLoalMap究竟是什么?

ThreadLoalMap

本文分析的是1.7的源碼。

從名字上看,可以猜到它也是一個(gè)類似HashMap的數(shù)據(jù)結(jié)構(gòu),但是在ThreadLocal中,并沒(méi)實(shí)現(xiàn)Map接口。

在ThreadLoalMap中,也是初始化一個(gè)大小16的Entry數(shù)組,Entry對(duì)象用來(lái)保存每一個(gè)key-value鍵值對(duì),只不過(guò)這里的key永遠(yuǎn)都是ThreadLocal對(duì)象,是不是很神奇,通過(guò)ThreadLocal對(duì)象的set方法,結(jié)果把ThreadLocal對(duì)象自己當(dāng)做key,放進(jìn)了ThreadLoalMap中。

這里需要注意的是,ThreadLoalMap的Entry是繼承WeakReference,和HashMap很大的區(qū)別是,Entry中沒(méi)有next字段,所以就不存在鏈表的情況了。

hash沖突

沒(méi)有鏈表結(jié)構(gòu),那發(fā)生hash沖突了怎么辦?

先看看ThreadLoalMap中插入一個(gè)key-value的實(shí)現(xiàn)

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();

        if (k == key) {
            e.value = value;
            return;
        }

        if (k == null) {
            replaceStaleEntry(key, value, i);
            return;
        }
    }

    tab[i] = new Entry(key, value);
    int sz = ++size;
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
}

每個(gè)ThreadLocal對(duì)象都有一個(gè)hash值threadLocalHashCode,每初始化一個(gè)ThreadLocal對(duì)象,hash值就增加一個(gè)固定的大小0x61c88647。

在插入過(guò)程中,根據(jù)ThreadLocal對(duì)象的hash值,定位到table中的位置i,過(guò)程如下:

1、如果當(dāng)前位置是空的,那么正好,就初始化一個(gè)Entry對(duì)象放在位置i上;

2、不巧,位置i已經(jīng)有Entry對(duì)象了,如果這個(gè)Entry對(duì)象的key正好是即將設(shè)置的key,那么重新設(shè)置Entry中的value;

3、很不巧,位置i的Entry對(duì)象,和即將設(shè)置的key沒(méi)關(guān)系,那么只能找下一個(gè)空位置;

這樣的話,在get的時(shí)候,也會(huì)根據(jù)ThreadLocal對(duì)象的hash值,定位到table中的位置,然后判斷該位置Entry對(duì)象中的key是否和get的key一致,如果不一致,就判斷下一個(gè)位置

可以發(fā)現(xiàn),set和get如果沖突嚴(yán)重的話,效率很低,因?yàn)門hreadLoalMap是Thread的一個(gè)屬性,所以即使在自己的代碼中控制了設(shè)置的元素個(gè)數(shù),但還是不能控制其它代碼的行為。

內(nèi)存泄露

ThreadLocal可能導(dǎo)致內(nèi)存泄漏,為什么?

先看看Entry的實(shí)現(xiàn):

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

通過(guò)之前的分析已經(jīng)知道,當(dāng)使用ThreadLocal保存一個(gè)value時(shí),會(huì)在ThreadLocalMap中的數(shù)組插入一個(gè)Entry對(duì)象,按理說(shuō)key-value都應(yīng)該以強(qiáng)引用保存在Entry對(duì)象中,但在ThreadLocalMap的實(shí)現(xiàn)中,key被保存到了WeakReference對(duì)象中。

這就導(dǎo)致了一個(gè)問(wèn)題,ThreadLocal在沒(méi)有外部強(qiáng)引用時(shí),發(fā)生GC時(shí)會(huì)被回收,如果創(chuàng)建ThreadLocal的線程一直持續(xù)運(yùn)行,那么這個(gè)Entry對(duì)象中的value就有可能一直得不到回收,發(fā)生內(nèi)存泄露。

如何避免內(nèi)存泄露

既然已經(jīng)發(fā)現(xiàn)有內(nèi)存泄露的隱患,自然有應(yīng)對(duì)的策略,在調(diào)用ThreadLocal的get()、set()可能會(huì)清除ThreadLocalMap中key為null的Entry對(duì)象,這樣對(duì)應(yīng)的value就沒(méi)有GC Roots可達(dá)了,下次GC的時(shí)候就可以被回收,當(dāng)然如果調(diào)用remove方法,肯定會(huì)刪除對(duì)應(yīng)的Entry對(duì)象。

如果使用ThreadLocal的set方法之后,沒(méi)有顯示的調(diào)用remove方法,就有可能發(fā)生內(nèi)存泄露,所以養(yǎng)成良好的編程習(xí)慣十分重要,使用完ThreadLocal之后,記得調(diào)用remove方法。

ThreadLocal<String> localName = new ThreadLocal();
try {
    localName.set("占小狼");
    // 其它業(yè)務(wù)邏輯
} finally {
    localName.remove();
}

總結(jié)

到此這篇關(guān)于Java面試必問(wèn)之ThreadLocal終極篇的文章就介紹到這了,更多相關(guān)Java面試ThreadLocal內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot自定義配置項(xiàng)過(guò)程

    SpringBoot自定義配置項(xiàng)過(guò)程

    在SpringBoot項(xiàng)目中,通過(guò)在application.properties文件中添加配置項(xiàng),然后使用@ConfigurationProperties注解將這些配置項(xiàng)與實(shí)體Bean進(jìn)行綁定,可以實(shí)現(xiàn)配置項(xiàng)與實(shí)體類字段的自動(dòng)關(guān)聯(lián),進(jìn)而方便地讀取配置文件中的數(shù)據(jù),這種方法不僅簡(jiǎn)化了配置管理
    2024-11-11
  • Spring Boot Web 開發(fā)注解篇

    Spring Boot Web 開發(fā)注解篇

    在 Spring Boot 快速入門中,只要在 pom.xml 加入了 spring-boot-starter-web 依賴,即可快速開發(fā) web 應(yīng)用。下文給大家詳細(xì)介紹了spring boot web 開發(fā)注解,感興趣的朋友參考下吧
    2017-08-08
  • Java實(shí)現(xiàn)簡(jiǎn)易計(jì)算器(逆波蘭表達(dá)式)

    Java實(shí)現(xiàn)簡(jiǎn)易計(jì)算器(逆波蘭表達(dá)式)

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)易計(jì)算器,逆波蘭表達(dá)式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • Java線程池中的各個(gè)參數(shù)如何合理設(shè)置

    Java線程池中的各個(gè)參數(shù)如何合理設(shè)置

    這篇文章主要介紹了Java線程池中的各個(gè)參數(shù)如何合理設(shè)置操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Java中精確的浮點(diǎn)運(yùn)算操作示例

    Java中精確的浮點(diǎn)運(yùn)算操作示例

    這篇文章主要介紹了Java中精確的浮點(diǎn)運(yùn)算操作方法,結(jié)合具體實(shí)例形式分析了java浮點(diǎn)數(shù)運(yùn)算的相關(guān)函數(shù)、使用技巧與注意事項(xiàng),需要的朋友可以參考下
    2017-06-06
  • Java 異步線程監(jiān)聽與結(jié)果回調(diào)及異常捕獲總結(jié)分析

    Java 異步線程監(jiān)聽與結(jié)果回調(diào)及異常捕獲總結(jié)分析

    異常是程序之中導(dǎo)致程序中斷的一種指令流,異常一旦出現(xiàn)并且沒(méi)有進(jìn)行合理處理的話,那么程序就將中斷執(zhí)行,這篇文章綜合介紹了異步線程監(jiān)聽與結(jié)果回調(diào)及異常捕獲
    2021-11-11
  • SpringBoot集成內(nèi)存數(shù)據(jù)庫(kù)hsqldb的實(shí)踐

    SpringBoot集成內(nèi)存數(shù)據(jù)庫(kù)hsqldb的實(shí)踐

    hsqldb只需要添加對(duì)應(yīng)的依賴,然后在配置文件進(jìn)行配置。不需要安裝一個(gè)數(shù)據(jù)庫(kù),本文就來(lái)介紹一下具體使用,感興趣的可以了解一下
    2021-09-09
  • Spring?Security實(shí)現(xiàn)基于RBAC的權(quán)限表達(dá)式動(dòng)態(tài)訪問(wèn)控制的操作方法

    Spring?Security實(shí)現(xiàn)基于RBAC的權(quán)限表達(dá)式動(dòng)態(tài)訪問(wèn)控制的操作方法

    這篇文章主要介紹了Spring?Security實(shí)現(xiàn)基于RBAC的權(quán)限表達(dá)式動(dòng)態(tài)訪問(wèn)控制,資源權(quán)限表達(dá)式動(dòng)態(tài)權(quán)限控制在Spring Security也是可以實(shí)現(xiàn)的,首先開啟方法級(jí)別的注解安全控制,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-04-04
  • Java8中的forEach使用及說(shuō)明

    Java8中的forEach使用及說(shuō)明

    這篇文章主要介紹了Java8中的forEach使用及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • SpringBoot+STOMP協(xié)議實(shí)現(xiàn)私聊、群聊

    SpringBoot+STOMP協(xié)議實(shí)現(xiàn)私聊、群聊

    本文將結(jié)合實(shí)例代碼,介紹SpringBoot+STOMP協(xié)議實(shí)現(xiàn)私聊、群聊,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-06-06

最新評(píng)論

玛纳斯县| 临江市| 东平县| 多伦县| 洛南县| 丘北县| 邵阳县| 榆社县| 新郑市| 大安市| 金堂县| 静宁县| 瑞昌市| 伊春市| 肃北| 辉南县| 元阳县| 榆林市| 全南县| 河南省| 平原县| 临清市| 正宁县| 神木县| 肇庆市| 拉萨市| 无极县| 都兰县| 三亚市| 海安县| 台前县| 永康市| 景东| 修文县| 新乡市| 贵定县| 宜章县| 永兴县| 安平县| 吴旗县| 阳江市|