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

Java ThreadLocal的使用詳解

 更新時(shí)間:2021年05月14日 14:48:19   作者:KerryWu  
ThreadLocal是線程私有的局部變量存儲容器,可以理解成每個(gè)線程都有自己專屬的存儲容器,用來存儲線程私有變量。ThreadLocal 在日常開發(fā)框架中應(yīng)用廣泛,但用不好也會出現(xiàn)各種問題,本文就此講解一下。

ThreadLocal是線程私有的局部變量存儲容器,可以理解成每個(gè)線程都有自己專屬的存儲容器,用來存儲線程私有變量。ThreadLocal 在日常開發(fā)框架中應(yīng)用廣泛,但用不好也會出現(xiàn)各種問題,本文就此講解一下。

1. 應(yīng)用場景

ThreadLocal 的常見應(yīng)用場景有兩種:

  1. 多線程并發(fā)場景中,用來保障線程安全。
  2. 處理較為復(fù)雜的業(yè)務(wù)時(shí),使用ThreadLocal代替參數(shù)的顯示傳遞。

1.1. 保障線程安全

多線程訪問同一個(gè)共享變量的時(shí)候容易出現(xiàn)并發(fā)問題,特別是多個(gè)線程對一個(gè)變量進(jìn)行寫入的時(shí)候,為了保證線程安全,一般使用者在訪問共享變量的時(shí)候需要進(jìn)行額外的同步措施才能保證線程安全性,如:synchronized、Lock之類的鎖。

ThreadLocal是除了加鎖這種同步方式之外的一種,規(guī)避多線程訪問出現(xiàn)線程不安全的方法。當(dāng)我們在創(chuàng)建一個(gè)變量后,如果每個(gè)線程對其進(jìn)行訪問的時(shí)候訪問的都是線程自己的變量,這樣就不會存在線程不安全問題。

ThreadLocal是JDK包提供的,它提供線程本地變量,如果創(chuàng)建一個(gè)ThreadLocal變量,那么訪問這個(gè)變量的每個(gè)線程都會有這個(gè)變量的一個(gè)副本,在實(shí)際多線程操作的時(shí)候,操作的是自己本地內(nèi)存中的變量,從而規(guī)避了線程安全問題。

1.2. 顯示傳遞參數(shù)

這里舉幾個(gè)例子:

示例1:獲取接口的當(dāng)前請求用戶
在后臺接口業(yè)務(wù)邏輯的全過程中,如果需要在多個(gè)地方獲取當(dāng)前請求用戶的信息。通常的一種做法就是:在接口請求時(shí),通過過濾器、攔截器、AOP等方式,從session或token中獲取當(dāng)前用戶信息,存入ThreadLocal中。

在整個(gè)接口處理過程中,如果沒有另外創(chuàng)建線程,都可以直接從ThreadLocal變量中獲取當(dāng)前用戶,而無需再從Session、token中驗(yàn)證和獲取用戶。這種方案設(shè)計(jì)不僅提高性能,最重要的是將原本復(fù)雜的邏輯和代碼實(shí)現(xiàn),變得簡潔明了。例如下面的這個(gè)例子:

(1)定義ThreadLocal變量:UserProfileThread.java

public class UserProfileThread {
    private static ThreadLocal<UserProfile> USER_PROFILE_TL =new ThreadLocal<>();

    public static void  setUserProfile(UserProfile userProfile){
        USER_PROFILE_TL.set(userProfile);
    }

    public static UserProfile getUserProfile() {
        return USER_PROFILE_TL.get();
    }

    public static String getCurrentUser() {
        return Optional.ofNullable(USER_PROFILE_TL.get())
                .map(UserProfile::getUid)
                .orElse(UserProfile.ANONYMOUS_USER);
    }
}

(2)在過濾器中設(shè)置變量值:

   @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        UserProfile userProfile = null;
        // ... 驗(yàn)證和獲取用戶信息 userProfile
        UserProfileThread.setUserProfile(userProfile);
        filterChain.doFilter(servletRequest, servletResponse);
    }

(3)獲取當(dāng)前用戶信息

//獲取當(dāng)前用戶
String uid=UserProfileThread.getCurrentUser();
//獲取當(dāng)前用戶對象
UserProfile user=UserProfileThread.getUserProfile();

示例2:spring框架中保證數(shù)據(jù)庫事務(wù)在同一個(gè)連接下執(zhí)行

要想實(shí)現(xiàn)jdbc事務(wù), 就必須是在同一個(gè)連接對象中操作,多個(gè)連接下事務(wù)就會不可控,需要借助分布式事務(wù)完成。那spring框架如何保證數(shù)據(jù)庫事務(wù)在同一個(gè)連接下執(zhí)行的呢?

DataSourceTransactionManager 是spring的數(shù)據(jù)源事務(wù)管理器,它會在你調(diào)用getConnection()的時(shí)候從數(shù)據(jù)庫連接池中獲取一個(gè)connection, 然后將其與ThreadLocal綁定,事務(wù)完成后解除綁定。這樣就保證了事務(wù)在同一連接下完成。

2. 實(shí)現(xiàn)原理

ThreadLocal類提供set/get方法存儲和獲取value值,但實(shí)際上ThreadLocal類并不存儲value值,真正存儲是靠ThreadLocalMap這個(gè)類。

每個(gè)線程實(shí)例都對應(yīng)一個(gè)TheadLocalMap實(shí)例,我們可以在同一個(gè)線程里實(shí)例化很多個(gè)ThreadLocal來存儲很多種類型的值,這些ThreadLocal實(shí)例分別作為key,對應(yīng)各自的value,最終存儲在Entry table數(shù)組中。
我們看看ThreadLocal的set方法:

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

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

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    // 省略其他方法
}

set的邏輯比較簡單,就是獲取當(dāng)前線程的ThreadLocalMap,然后往map里添加KV,K是當(dāng)前ThreadLocal實(shí)例,V是我們傳入的value。這里需要注意一下,map的獲取是需要從Thread類對象里面取,看一下Thread類的定義。

public class Thread implements Runnable {
    ThreadLocal.ThreadLocalMap threadLocals = null;
    //省略其他
}

Thread類維護(hù)了一個(gè)ThreadLocalMap的變量引用。

因此,我們可以得出如下結(jié)論:

  1. 每個(gè)線程是一個(gè)Thread實(shí)例,其內(nèi)部維護(hù)一個(gè)threadLocals的實(shí)例成員,其類型是ThreadLocal.ThreadLocalMap。
  2. ThreadLocal本身并不是一個(gè)容器,我們存取的value實(shí)際上存儲在ThreadLocalMap中,ThreadLocal只是作為TheadLocalMap的key。

3. 注意事項(xiàng)

ThreadLocal實(shí)例有提供remove()方法,用于回收對象,清除對應(yīng)的內(nèi)存占用。這個(gè)方法通常容易被忽略,而導(dǎo)致出現(xiàn)了各種問題。如下面幾種:

  • 線程復(fù)用:在“獲取接口的當(dāng)前請求用戶”的例子中,Tomcat中是通過線程池來處理用戶請求的,而線程池中線程是復(fù)用的??隙〞霈F(xiàn)一個(gè)線程前后被不同用戶的接口請求復(fù)用的情況,因此需要對用過的ThreaLocal變量進(jìn)行覆蓋或清除。
  • 內(nèi)存溢出:由于ThreadLocalMap的生命周期跟Thread一樣長,如果創(chuàng)建的ThreadLocal變量很多,即對應(yīng)的key占用的內(nèi)存很大,但卻沒有手動刪除,到了一定程度就會導(dǎo)致內(nèi)存泄漏。

以上就是Java ThreadLocal的使用詳解的詳細(xì)內(nèi)容,更多關(guān)于Java ThreadLocal的使用的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Mybatis通用Mapper(tk.mybatis)的使用

    Mybatis通用Mapper(tk.mybatis)的使用

    本文主要介紹了Mybatis通用Mapper(tk.mybatis)的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • 圖解如何在Spring Boot中使用JSP頁面

    圖解如何在Spring Boot中使用JSP頁面

    這篇文章主要介紹了圖解如何在Spring Boot中使用JSP頁面,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Spring Boot Web 靜態(tài)文件緩存處理的方法

    Spring Boot Web 靜態(tài)文件緩存處理的方法

    本篇文章主要介紹了Spring Boot Web 靜態(tài)文件緩存處理的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-02-02
  • SpringBoot+redis配置及測試的方法

    SpringBoot+redis配置及測試的方法

    這篇文章主要介紹了SpringBoot+redis配置及測試的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • Java中SimpleDateFormat方法超詳細(xì)分析

    Java中SimpleDateFormat方法超詳細(xì)分析

    這篇文章主要給大家介紹了關(guān)于Java中SimpleDateFormat方法超詳細(xì)分析的相關(guān)資料,SimpleDateFormat 是一個(gè)以國別敏感的方式格式化和分析數(shù)據(jù)的具體類,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-08-08
  • SpringBoot2 task scheduler 定時(shí)任務(wù)調(diào)度器四種方式

    SpringBoot2 task scheduler 定時(shí)任務(wù)調(diào)度器四種方式

    這篇文章主要介紹了SpringBoot2 task scheduler 定時(shí)任務(wù)調(diào)度器四種方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • 用Java打印九九除法表代碼分析

    用Java打印九九除法表代碼分析

    這篇文章主要介紹了如何用Java語言打印九九除法表,包括其使用的源代碼,需要的朋友可以參考下。
    2017-08-08
  • Java代碼里如何拼接SQL語句到mybatis的xml

    Java代碼里如何拼接SQL語句到mybatis的xml

    這篇文章主要介紹了Java代碼里拼接SQL語句到mybatis的xml操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • 搭建maven私有倉庫的方法實(shí)現(xiàn)

    搭建maven私有倉庫的方法實(shí)現(xiàn)

    Maven是一個(gè)流行的Java項(xiàng)目管理工具,它可以幫助我們管理項(xiàng)目的構(gòu)建、報(bào)告和文檔,本文主要介紹了搭建maven私有倉庫的方法實(shí)現(xiàn),感興趣的可以了解一下
    2023-05-05
  • spring boot 開發(fā)soap webservice的實(shí)現(xiàn)代碼

    spring boot 開發(fā)soap webservice的實(shí)現(xiàn)代碼

    這篇文章主要介紹了spring boot 開發(fā)soap webservice的實(shí)現(xiàn)代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-01-01

最新評論

将乐县| 大丰市| 武宣县| 上杭县| 南丰县| 依安县| 勐海县| 武陟县| 镶黄旗| 华阴市| 上高县| 清流县| 富宁县| 衡水市| 安岳县| 石河子市| 攀枝花市| 古交市| 绥化市| 淮滨县| 贵港市| 兴仁县| 阜平县| 扎赉特旗| 巴东县| 江陵县| 遵义市| 衡阳县| 东安县| 万安县| 敦化市| 云安县| 通城县| 特克斯县| 彰武县| 拜城县| 钟山县| 松原市| 南康市| 彰武县| 海宁市|