亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

解決ThreadLocal獲取不到值大坑

 更新時(shí)間:2023年05月08日 11:09:53   作者:我是小趴菜  
這篇文章主要介紹了解決ThreadLocal獲取不到值大坑

1:問題起因

今天項(xiàng)目上測試環(huán)境,再給領(lǐng)導(dǎo)演示的時(shí)候出現(xiàn)了bug,很尷尬。于是我跟前端同學(xué)通過模擬請求,最后發(fā)現(xiàn)在調(diào)一個(gè)接口的時(shí)候返回了一個(gè) token為空 的錯(cuò)誤。

但是前端同學(xué)說傳了token了,那為什么還會報(bào)token為空的錯(cuò)誤呢

我們項(xiàng)目使用的JWT生成用戶token,每次請求都要經(jīng)過攔截器校驗(yàn)。因?yàn)樵谡埱蟮臅r(shí)候我們經(jīng)常需要用到當(dāng)前用戶登錄的ID,所以我們使用到了ThhreadLocal這個(gè)工具類。

Map<String, Claim> claimMap = JwtUtil.verifyToken(headerToken);
if (null == claimMap) {
    throw new GlobalException(ResponseEnums.TOKEN_INVALID_ERROR);
}
//將參數(shù)放入上下文中
Map<String, Object> result = new HashMap<>();
Set<Map.Entry<String, Claim>> entrySet = claimMap.entrySet();
for (Map.Entry<String, Claim> claimEntry : entrySet) {
    result.put(claimEntry.getKey(), claimEntry.getValue().asString());
}
//將用戶ID存到ThreadLocal中,以便后續(xù)的獲取使用
ThreadLocalUtil.getInstance().setContext(result);

這里也貼上ThreadLocalUtil工具類代碼

@Slf4j
public class ThreadLocalUtil {
    private static final ThreadLocal<Map<String, Object>> CONTEXT = new ThreadLocal<>();
    private ThreadLocalUtil() {}
    public static ThreadLocalUtil getInstance() {
        return SingletonHolder.INSTANCE;
    }
    private static class SingletonHolder {
        private static final ThreadLocalUtil INSTANCE = new ThreadLocalUtil();
    }
    public void setContext(Map<String, Object> map) {
        CONTEXT.set(map);
    }
    public static Map<String, Object> getContext() {
        return CONTEXT.get();
    }
    public void clear() {
        CONTEXT.remove();
    }
    public static Integer getUserId() {
        Map<String, Object> context = getContext();
        if(context == null || !context.containsKey(JwtUtil.USER_ID)) {
            throw new GlobalException(ResponseEnums.TOKEN_IS_NULL_ERROR);
        }
        return Integer.parseInt(String.valueOf(context.get(JwtUtil.USER_ID)));
    }
}

2:問題復(fù)現(xiàn)

我們寫一個(gè)簡單的測試

public static void main(String[] args) {
    HashMap<String,Object> map = new HashMap<>();
    map.put(JwtUtil.USER_ID,"1");
    ThreadLocalUtil.getInstance().setContext(map);
    System.out.println(ThreadLocalUtil.getUserId());
}

可以看到可以拿到我們設(shè)置的值。

但是如果將ThreadLocal跟Java8的Stream一起配合使用呢?

public static void main(String[] args) {
    HashMap<String,Object> map = new HashMap<>();
    map.put(JwtUtil.USER_ID,"1");
    ThreadLocalUtil.getInstance().setContext(map);
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    //ThreadLocal獲取值放在stream里面執(zhí)行
    list.parallelStream().filter(x -> x.equals(ThreadLocalUtil.getUserId())).collect(Collectors.toList());
}

我們的問題復(fù)現(xiàn)了,報(bào)了token為空的錯(cuò)誤。但是這還是隨機(jī)會出現(xiàn)的情況,并不是每次都會出現(xiàn)。所以導(dǎo)致我們在調(diào)試的時(shí)候并沒有出現(xiàn)這個(gè)問題

3:分析問題

咋一看并不能知道為什么會這樣,所以我在獲取用戶id的打印了一下日志

看出問題了吧,竟然有三個(gè)線程來獲取,因?yàn)槲覀冊O(shè)置值的線程就是main線程,所以前面二個(gè)線程獲取到的值就是空的,所以就拋出了異常

所以現(xiàn)在只需要知道這個(gè) ForkJoinPool是在哪就好了,最終在翻看源碼,找到原來就是在jdk8的Stream里面。

這是為什么呢?因?yàn)镴dk8的Stream底層使用了ForkJoinPool線程池,這就導(dǎo)致當(dāng)我們調(diào)用 ThreadLocalUtil.getUserId()的時(shí)候,是直接提交到了ForkJoinPool線程池中去了,這時(shí)候就會有其它線程去調(diào)用這個(gè)方法,所以就拿不到值了

4:如何解決

解決辦法就很簡單了,只需要把ThreadLocalUtil.getUserId()單獨(dú)拿出來執(zhí)行就可以了

public static void main(String[] args) {
    HashMap<String,Object> map = new HashMap<>();
    map.put(JwtUtil.USER_ID,"1");
    ThreadLocalUtil.getInstance().setContext(map);
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    //單獨(dú)拿出來執(zhí)行
    Integer userId = ThreadLocalUtil.getUserId();
    list.parallelStream().filter(x -> x.equals(userId)).collect(Collectors.toList());
}

所以當(dāng)你項(xiàng)目使用到了ThreadLocal的時(shí)候,切記要單獨(dú)使用,否則指不定就出現(xiàn)跟我一樣的問題了

以上就是解決ThreadLocal獲取不到值大坑的詳細(xì)內(nèi)容,更多關(guān)于ThreadLocal獲取不到值解決的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java反射的應(yīng)用之動(dòng)態(tài)代理深入理解

    Java反射的應(yīng)用之動(dòng)態(tài)代理深入理解

    這篇文章主要介紹了Java反射的應(yīng)用之動(dòng)態(tài)代理深入理解,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09
  • Spring Boot常見外部配置文件方式詳析

    Spring Boot常見外部配置文件方式詳析

    這篇文章主要給大家介紹了關(guān)于Spring Boot常見外部配置文件方式的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者使用Spring Boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Java如何隨機(jī)生成圖片驗(yàn)證碼

    Java如何隨機(jī)生成圖片驗(yàn)證碼

    這篇文章主要為大家詳細(xì)介紹了Java如何隨機(jī)生成圖片驗(yàn)證碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • java讀取http請求中的body實(shí)例代碼

    java讀取http請求中的body實(shí)例代碼

    下面小編就為大家?guī)硪黄猨ava讀取http請求中的body實(shí)例代碼。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-09-09
  • Java經(jīng)典面試題匯總:網(wǎng)絡(luò)編程

    Java經(jīng)典面試題匯總:網(wǎng)絡(luò)編程

    本篇總結(jié)的是Java 網(wǎng)絡(luò)編程相關(guān)的面試題,后續(xù)會持續(xù)更新,希望我的分享可以幫助到正在備戰(zhàn)面試的實(shí)習(xí)生或者已經(jīng)工作的同行,如果發(fā)現(xiàn)錯(cuò)誤還望大家多多包涵,不吝賜教,謝謝
    2021-07-07
  • Java如何獲取JSON中某個(gè)對象的值

    Java如何獲取JSON中某個(gè)對象的值

    這篇文章主要介紹了Java如何獲取JSON中某個(gè)對象的值,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • ActiveMQ中consumer的消息確認(rèn)機(jī)制詳解

    ActiveMQ中consumer的消息確認(rèn)機(jī)制詳解

    這篇文章主要介紹了ActiveMQ中consumer的消息確認(rèn)機(jī)制詳解,對于broker而言,只有接收到確認(rèn)指令,才會認(rèn)為消息被正確的接收或者處理成功了,InforSuiteMQ提供以下幾種Consumer與Broker之間的消息確認(rèn)方式,需要的朋友可以參考下
    2023-10-10
  • SpringBoot接口實(shí)現(xiàn)百萬并發(fā)的代碼示例

    SpringBoot接口實(shí)現(xiàn)百萬并發(fā)的代碼示例

    隨著互聯(lián)網(wǎng)的發(fā)展,越來越多的應(yīng)用需要支持高并發(fā),在這種情況下,如何實(shí)現(xiàn)高并發(fā)成為了一個(gè)重要的問題,Spring Boot是一個(gè)非常流行的Java框架,它提供了很多方便的功能來支持高并發(fā),本文將介紹如何使用Spring Boot來實(shí)現(xiàn)百萬并發(fā)
    2023-10-10
  • idea提交文件時(shí)如何忽略某些文件的提交

    idea提交文件時(shí)如何忽略某些文件的提交

    這篇文章主要介紹了idea提交文件時(shí)如何忽略某些文件的提交問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • Dom4j解析XML_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Dom4j解析XML_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要介紹了Dom4j解析XML,dom4j是一個(gè)Java的XML API,類似于jdom,用來讀寫XML文件的,有興趣的可以了解一下
    2017-07-07

最新評論