解决ThreadLocal获取不到值大坑

这篇文章主要介绍了解决ThreadLocal获取不到值大坑

1:问题起因

今天项目上测试环境,再给领导演示的时候出现了bug,很尴尬。于是我跟前端同学通过模拟请求,最后发现在调一个接口的时候返回了一个 token为空 的错误。

但是前端同学说传了token了,那为什么还会报token为空的错误呢

我们项目使用的JWT生成用户token,每次请求都要经过拦截器校验。因为在请求的时候我们经常需要用到当前用户登录的ID,所以我们使用到了ThhreadLocal这个工具类。

Map claimMap = JwtUtil.verifyToken(headerToken); if (null == claimMap) { throw new GlobalException(ResponseEnums.TOKEN_INVALID_ERROR); } //将参数放入上下文中 Map result = new HashMap<>(); Set> entrySet = claimMap.entrySet(); for (Map.Entry claimEntry : entrySet) { result.put(claimEntry.getKey(), claimEntry.getValue().asString()); } //将用户ID存到ThreadLocal中,以便后续的获取使用 ThreadLocalUtil.getInstance().setContext(result);

这里也贴上ThreadLocalUtil工具类代码

@Slf4j public class ThreadLocalUtil { private static final ThreadLocal> 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 map) { CONTEXT.set(map); } public static Map getContext() { return CONTEXT.get(); } public void clear() { CONTEXT.remove(); } public static Integer getUserId() { Map 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:问题复现

我们写一个简单的测试

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

我们的问题复现了,报了token为空的错误。但是这还是随机会出现的情况,并不是每次都会出现。所以导致我们在调试的时候并没有出现这个问题

3:分析问题

咋一看并不能知道为什么会这样,所以我在获取用户id的打印了一下日志

看出问题了吧,竟然有三个线程来获取,因为我们设置值的线程就是main线程,所以前面二个线程获取到的值就是空的,所以就抛出了异常

所以现在只需要知道这个 ForkJoinPool是在哪就好了,最终在翻看源码,找到原来就是在jdk8的Stream里面。

这是为什么呢?因为Jdk8的Stream底层使用了ForkJoinPool线程池,这就导致当我们调用 ThreadLocalUtil.getUserId()的时候,是直接提交到了ForkJoinPool线程池中去了,这时候就会有其它线程去调用这个方法,所以就拿不到值了

4:如何解决

解决办法就很简单了,只需要把ThreadLocalUtil.getUserId()单独拿出来执行就可以了

public static void main(String[] args) { HashMap map = new HashMap<>(); map.put(JwtUtil.USER_ID,"1"); ThreadLocalUtil.getInstance().setContext(map); List list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); //单独拿出来执行 Integer userId = ThreadLocalUtil.getUserId(); list.parallelStream().filter(x -> x.equals(userId)).collect(Collectors.toList()); }

所以当你项目使用到了ThreadLocal的时候,切记要单独使用,否则指不定就出现跟我一样的问题了

以上就是解决ThreadLocal获取不到值大坑的详细内容,更多关于ThreadLocal获取不到值解决的资料请关注0133技术站其它相关文章!

以上就是解决ThreadLocal获取不到值大坑的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » Java