解決ThreadLocal獲取不到值大坑
1:問題起因
今天項目上測試環(huán)境,再給領導演示的時候出現(xiàn)了bug,很尷尬。于是我跟前端同學通過模擬請求,最后發(fā)現(xiàn)在調一個接口的時候返回了一個 token為空 的錯誤。
但是前端同學說傳了token了,那為什么還會報token為空的錯誤呢
我們項目使用的JWT生成用戶token,每次請求都要經(jīng)過攔截器校驗。因為在請求的時候我們經(jīng)常需要用到當前用戶登錄的ID,所以我們使用到了ThhreadLocal這個工具類。
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:問題復現(xiàn)
我們寫一個簡單的測試
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());
}
可以看到可以拿到我們設置的值。
但是如果將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());
}
我們的問題復現(xiàn)了,報了token為空的錯誤。但是這還是隨機會出現(xiàn)的情況,并不是每次都會出現(xiàn)。所以導致我們在調試的時候并沒有出現(xiàn)這個問題
3:分析問題
咋一看并不能知道為什么會這樣,所以我在獲取用戶id的打印了一下日志

看出問題了吧,竟然有三個線程來獲取,因為我們設置值的線程就是main線程,所以前面二個線程獲取到的值就是空的,所以就拋出了異常

所以現(xiàn)在只需要知道這個 ForkJoinPool是在哪就好了,最終在翻看源碼,找到原來就是在jdk8的Stream里面。
這是為什么呢?因為Jdk8的Stream底層使用了ForkJoinPool線程池,這就導致當我們調用 ThreadLocalUtil.getUserId()的時候,是直接提交到了ForkJoinPool線程池中去了,這時候就會有其它線程去調用這個方法,所以就拿不到值了
4:如何解決
解決辦法就很簡單了,只需要把ThreadLocalUtil.getUserId()單獨拿出來執(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);
//單獨拿出來執(zhí)行
Integer userId = ThreadLocalUtil.getUserId();
list.parallelStream().filter(x -> x.equals(userId)).collect(Collectors.toList());
}
所以當你項目使用到了ThreadLocal的時候,切記要單獨使用,否則指不定就出現(xiàn)跟我一樣的問題了
以上就是解決ThreadLocal獲取不到值大坑的詳細內容,更多關于ThreadLocal獲取不到值解決的資料請關注腳本之家其它相關文章!
相關文章
Java中JDom解析XML_動力節(jié)點Java學院整理
JDOM是一種解析XML的Java工具包。DOM適合于當今流行的各種語言,包括Java,JavaScripte,VB,VBScript,Perl,C,C++等。下面通過本文給大家介紹Java中JDom解析XML的方法,感興趣的朋友一起學習吧2017-07-07
springboot+thymeleaf找不到視圖的解決方案
這篇文章主要介紹了springboot+thymeleaf找不到視圖的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
spring框架下@value注解屬性static無法獲取值問題
這篇文章主要介紹了spring框架下@value注解屬性static無法獲取值問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11

