国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

ThreadLocal詳解

2501207950 / 2615人閱讀

摘要:在方法中取出開始時間,并計算耗時。是一個數組主要用來保存具體的數據,是的大小,而這表示當中元素數量超過該值時,就會擴容。如果這個剛好就是當前對象,則直接修改該位置上對象的。

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

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

簡單使用

1.創建ThreadLocal,只需要new一個ThreadLocal對象即可。

private ThreadLocal myThreadLocal = new ThreadLocal();

2.設置值

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

3.獲取值

myThreadLocal.get();

4.清除,有些情況下我們在使用完線程局部變量后,需要即時清理,否則會導致程序運行錯誤。

myThreadLocal.remove();

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

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

private ThreadLocal threadLocal = new ThreadLocal();

2.在@Before方法中記錄開始時間。

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

3.在@AfterReturning方法中取出開始時間,并計算耗時。

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

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

原理解析

其實ThreadLocal是個數據結構,下面我們就一起通過源碼來剖析一下ThreadLocal的運行原理。

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中。其實在每個線程中都維護著一個threadLocals變量(ThreadLocalMap類型),當使用set()方法的時候實際上是將值存在當前線程的threadLocals中的,調用get()方法也是從當前線程中取值的,這樣就做到了線程間的隔離。
看到這里想必你也奇怪,在設置值和取值的時候都沒有任何與key有關的東西,那么當一個線程有多個ThreadLocal的時候是如何做到一一對應的呢?那我們就一起來看下這個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中維護著tablesize以及threshold三個屬性。table是一個Entry數組主要用來保存具體的數據,sizetable的大小,而threshold這表示當table中元素數量超過該值時,table就會擴容。了解了ThreadLocalMap的結構之后,我們就來看下其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();
}

通過上面的代碼分析得出,整個的設值過程如下:

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

如果table中i這個位置是空的,那么就新創建一個Entry對象放置在i這個位置。

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

如果這個key剛好就是當前ThreadLocal對象,則直接修改該位置上Entry對象的value。

如果這個key不是當前TreadLocal對象,則尋找下一個位置的Entry對象,然后重復上述步驟進行判斷。

對于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);
    }
}

可見我們在初始化一個ThreadLocal對象的時候都為其會生成一個threadLocalHashCode值,每初始化一個ThreadLocal該值就增加0x61c88647。這樣就可以做到每個ThreadLocal在ThreadLocalMap中找到一個存儲值的位置了。

結束語

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

PS:學習不止,碼不停蹄!如果您喜歡我的文章,就關注我吧!

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/75800.html

相關文章

  • Java基礎進階之ThreadLocal詳解

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

    worldligang 評論0 收藏0
  • Java面試題必備知識之ThreadLocal

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

    Maxiye 評論0 收藏0
  • 阿里 2021 版最全 Java 并發編程筆記,看完我才懂了“內卷”的真正意義

    摘要:純分享直接上干貨操作系統并發支持進程管理內存管理文件系統系統進程間通信網絡通信阻塞隊列數組有界隊列鏈表無界隊列優先級有限無界隊列延時無界隊列同步隊列隊列內存模型線程通信機制內存共享消息傳遞內存模型順序一致性指令重排序原則內存語義線程 純分享 , 直接上干貨! 操作系統并發支持 進程管理內存管...

    不知名網友 評論0 收藏0
  • Java經典

    摘要:請注意,我們在聊聊單元測試遇到問題多思考多查閱多驗證,方能有所得,再勤快點樂于分享,才能寫出好文章。單元測試是指對軟件中的最小可測試單元進行檢查和驗證。 JAVA容器-自問自答學HashMap 這次我和大家一起學習HashMap,HashMap我們在工作中經常會使用,而且面試中也很頻繁會問到,因為它里面蘊含著很多知識點,可以很好的考察個人基礎。但一個這么重要的東西,我為什么沒有在一開始...

    xcold 評論0 收藏0
  • Java 總結

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

    caspar 評論0 收藏0

發表評論

0條評論

2501207950

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<