亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

ThreadLocal詳解

2501207950 / 2669人閱讀

摘要:在方法中取出開始時(shí)間,并計(jì)算耗時(shí)。是一個(gè)數(shù)組主要用來保存具體的數(shù)據(jù),是的大小,而這表示當(dāng)中元素?cái)?shù)量超過該值時(shí),就會(huì)擴(kuò)容。如果這個(gè)剛好就是當(dāng)前對(duì)象,則直接修改該位置上對(duì)象的。

想要獲取更多文章可以訪問我的博客?-?代碼無止境。
什么是ThreadLocal

ThreadLocal在《Java核心技術(shù) 卷一》中被稱作線程局部變量(PS:關(guān)注公眾號(hào)itweknow,回復(fù)“Java核心技術(shù)”獲取該書),我們可以利用ThreadLocal創(chuàng)建只能由同一線程讀和寫的變量。因此就算兩個(gè)線程正在執(zhí)行同一段代碼,并且這段代碼具有對(duì)ThreadLocal變量的引用,這兩個(gè)線程也無法看到彼此的ThreadLocal變量。

簡單使用

1.創(chuàng)建ThreadLocal,只需要new一個(gè)ThreadLocal對(duì)象即可。

private ThreadLocal myThreadLocal = new ThreadLocal();

2.設(shè)置值

myThreadLocal.set("I"m a threadLocal");

3.獲取值

myThreadLocal.get();

4.清除,有些情況下我們?cè)谑褂猛昃€程局部變量后,需要即時(shí)清理,否則會(huì)導(dǎo)致程序運(yùn)行錯(cuò)誤。

myThreadLocal.remove();

假如我們現(xiàn)在要利用AOP打印方法的耗時(shí),這個(gè)時(shí)候我們需要在@Before方法中記錄方法開始執(zhí)行的時(shí)間,然后在@AfterReturning方法中打印出來耗時(shí)時(shí)間。我們寫在切面里的方法可能慧在多個(gè)線程中同時(shí)執(zhí)行,所以此時(shí)我們需要ThreadLocal來記錄開始執(zhí)行的時(shí)間。

1.我們需要在切面類中定義一個(gè)ThreadLocal。

private ThreadLocal threadLocal = new ThreadLocal();

2.在@Before方法中記錄開始時(shí)間。

long startTime = System.currentTimeMillis();
threadLocal.set(startTime);

3.在@AfterReturning方法中取出開始時(shí)間,并計(jì)算耗時(shí)。

long startTime = threadLocal.get();
long spendTime = System.currentTimeMillis() - startTime;
threadLocal.remove();
System.out.println("方法執(zhí)行時(shí)間:" + spendTime + "ms");

這里只是借這個(gè)場(chǎng)景和大家一起熟悉一下ThreadLocal的用法,整個(gè)打印方法耗時(shí)的實(shí)現(xiàn)你可以在Github上找到,如果你想了解AOP可以參考這篇文章《使用 Spring Boot AOP 實(shí)現(xiàn) Web 日志處理和分布式鎖》。

原理解析

其實(shí)ThreadLocal是個(gè)數(shù)據(jù)結(jié)構(gòu),下面我們就一起通過源碼來剖析一下ThreadLocal的運(yùn)行原理。

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

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

上面是ThreadLocal的get()set()方法的源碼,可以看到ThreadLocal是將值存放在ThreadLocalMap中。其實(shí)在每個(gè)線程中都維護(hù)著一個(gè)threadLocals變量(ThreadLocalMap類型),當(dāng)使用set()方法的時(shí)候?qū)嶋H上是將值存在當(dāng)前線程的threadLocals中的,調(diào)用get()方法也是從當(dāng)前線程中取值的,這樣就做到了線程間的隔離。
看到這里想必你也奇怪,在設(shè)置值和取值的時(shí)候都沒有任何與key有關(guān)的東西,那么當(dāng)一個(gè)線程有多個(gè)ThreadLocal的時(shí)候是如何做到一一對(duì)應(yīng)的呢?那我們就一起來看下這個(gè)ThreadLocalMap類吧。

static class ThreadLocalMap {
    /**
     * The initial capacity -- MUST be a power of two.
     */
    private static final int INITIAL_CAPACITY = 16;

    /**
     * The table, resized as necessary.
     * table.length MUST always be a power of two.
     */
    private Entry[] table;

    /**
     * The number of entries in the table.
     */
    private int size = 0;

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

由上面可見在ThreadLocalMap中維護(hù)著table,size以及threshold三個(gè)屬性。table是一個(gè)Entry數(shù)組主要用來保存具體的數(shù)據(jù),sizetable的大小,而threshold這表示當(dāng)table中元素?cái)?shù)量超過該值時(shí),table就會(huì)擴(kuò)容。了解了ThreadLocalMap的結(jié)構(gòu)之后,我們就來看下其set方法吧。

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è)的設(shè)值過程如下:

通過ThreadLocal的threadLocalHashCode值定位到table中的位置i。

如果table中i這個(gè)位置是空的,那么就新創(chuàng)建一個(gè)Entry對(duì)象放置在i這個(gè)位置。

如果table中i這個(gè)位置不為空,則取出來i這個(gè)位置的key。

如果這個(gè)key剛好就是當(dāng)前ThreadLocal對(duì)象,則直接修改該位置上Entry對(duì)象的value。

如果這個(gè)key不是當(dāng)前TreadLocal對(duì)象,則尋找下一個(gè)位置的Entry對(duì)象,然后重復(fù)上述步驟進(jìn)行判斷。

對(duì)于get方法也是同樣的原理從ThreadLocalMap中獲取值。那么ThreadLocal是如何生成threadLocalHashCode值的呢?

public class ThreadLocal {
    private final int threadLocalHashCode = nextHashCode();
    private static final int HASH_INCREMENT = 0x61c88647;
    private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }
}

可見我們?cè)诔跏蓟粋€(gè)ThreadLocal對(duì)象的時(shí)候都為其會(huì)生成一個(gè)threadLocalHashCode值,每初始化一個(gè)ThreadLocal該值就增加0x61c88647。這樣就可以做到每個(gè)ThreadLocal在ThreadLocalMap中找到一個(gè)存儲(chǔ)值的位置了。

結(jié)束語

在文章的最后分享一次之前遇到的一個(gè)與ThreadLocal有關(guān)的坑,有一次在寫分頁的時(shí)候使用了PageHeler插件,引包的時(shí)候錯(cuò)誤地引用了MybatisPlus下的PagerHelper,而MybatisPlus下的PageHelper在ThreadLocal中存儲(chǔ)了SQL分頁信息在使用之后沒有移除,所以執(zhí)行了分頁的SQL之后在當(dāng)前線程中執(zhí)行的SQL都會(huì)出現(xiàn)問題。所以大家在使用ThreadLocal的過程中千萬要注意在適當(dāng)?shù)臅r(shí)候需要清除。本文主要介紹了Java中的線程局部變量ThreadLocal的使用,并且和大家一起稍微了解了一下源碼。希望對(duì)大家能夠有所幫助。

PS:學(xué)習(xí)不止,碼不停蹄!如果您喜歡我的文章,就關(guān)注我吧!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/75800.html

相關(guān)文章

  • Java基礎(chǔ)進(jìn)階之ThreadLocal詳解

    摘要:基本在項(xiàng)目開發(fā)中基本不會(huì)用到但是面試官是比較喜歡問這類問題的所以還是有必要了解一下該類的功能與原理的是什么是一個(gè)將在多線程中為每一個(gè)線程創(chuàng)建單獨(dú)的變量副本的類當(dāng)使用來維護(hù)變量時(shí)會(huì)為每個(gè)線程創(chuàng)建單獨(dú)的變量副本避免因多線程操作共享變量而導(dǎo)致的數(shù) ThreadLocal基本在項(xiàng)目開發(fā)中基本不會(huì)用到, 但是面試官是比較喜歡問這類問題的;所以還是有必要了解一下該類的功能與原理的. Thread...

    worldligang 評(píng)論0 收藏0
  • Java面試題必備知識(shí)之ThreadLocal

    摘要:方法,刪除當(dāng)前線程綁定的這個(gè)副本數(shù)字,這個(gè)值是的值,普通的是使用鏈表來處理沖突的,但是是使用線性探測(cè)法來處理沖突的,就是每次增加的步長,根據(jù)參考資料所說,選擇這個(gè)數(shù)字是為了讓沖突概率最小。 showImg(https://segmentfault.com/img/remote/1460000019828633); 老套路,先列舉下關(guān)于ThreadLocal常見的疑問,希望可以通過這篇學(xué)...

    Maxiye 評(píng)論0 收藏0
  • 阿里 2021 版最全 Java 并發(fā)編程筆記,看完我才懂了“內(nèi)卷”的真正意義

    摘要:純分享直接上干貨操作系統(tǒng)并發(fā)支持進(jìn)程管理內(nèi)存管理文件系統(tǒng)系統(tǒng)進(jìn)程間通信網(wǎng)絡(luò)通信阻塞隊(duì)列數(shù)組有界隊(duì)列鏈表無界隊(duì)列優(yōu)先級(jí)有限無界隊(duì)列延時(shí)無界隊(duì)列同步隊(duì)列隊(duì)列內(nèi)存模型線程通信機(jī)制內(nèi)存共享消息傳遞內(nèi)存模型順序一致性指令重排序原則內(nèi)存語義線程 純分享 , 直接上干貨! 操作系統(tǒng)并發(fā)支持 進(jìn)程管理內(nèi)存管...

    不知名網(wǎng)友 評(píng)論0 收藏0
  • Java經(jīng)典

    摘要:請(qǐng)注意,我們?cè)诹牧膯卧獪y(cè)試遇到問題多思考多查閱多驗(yàn)證,方能有所得,再勤快點(diǎn)樂于分享,才能寫出好文章。單元測(cè)試是指對(duì)軟件中的最小可測(cè)試單元進(jìn)行檢查和驗(yàn)證。 JAVA容器-自問自答學(xué)HashMap 這次我和大家一起學(xué)習(xí)HashMap,HashMap我們?cè)诠ぷ髦薪?jīng)常會(huì)使用,而且面試中也很頻繁會(huì)問到,因?yàn)樗锩嫣N(yùn)含著很多知識(shí)點(diǎn),可以很好的考察個(gè)人基礎(chǔ)。但一個(gè)這么重要的東西,我為什么沒有在一開始...

    xcold 評(píng)論0 收藏0
  • Java 總結(jié)

    摘要:中的詳解必修個(gè)多線程問題總結(jié)個(gè)多線程問題總結(jié)有哪些源代碼看了后讓你收獲很多,代碼思維和能力有較大的提升有哪些源代碼看了后讓你收獲很多,代碼思維和能力有較大的提升開源的運(yùn)行原理從虛擬機(jī)工作流程看運(yùn)行原理。 自己實(shí)現(xiàn)集合框架 (三): 單鏈表的實(shí)現(xiàn) 自己實(shí)現(xiàn)集合框架 (三): 單鏈表的實(shí)現(xiàn) 基于 POI 封裝 ExcelUtil 精簡的 Excel 導(dǎo)入導(dǎo)出 由于 poi 本身只是針對(duì)于 ...

    caspar 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<