首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >业务代码在线程池中乱使用java.lang.ThreadLocal变量,导致信息传递丢失的故障

业务代码在线程池中乱使用java.lang.ThreadLocal变量,导致信息传递丢失的故障

作者头像
崔认知
发布2023-06-19 15:18:21
发布2023-06-19 15:18:21
49400
代码可运行
举报
文章被收录于专栏:nobodynobody
运行总次数:0
代码可运行

现象


业务系统中,我们常常使用拦截器(spring中filter、HandlerInterceptor、aop)拦截登录校验,然后把登录用户的信息比如用户id放到一个

ThreadLocal的变量里。

隐蔽潜在故障


ThreadLocal变量在登录拦截器里设置的用户信息,在我的编码规范里必须控制在spring中的controller层,因为默认,controller层使用的线程与spring容器底层线程为同一个线程。

如果我们业务使用了线程池,而线程线提交的任务中又使用到了此ThreadLocal变量,会造成信息的丢失,导致故障的发生

示例:

代码语言:javascript
代码运行次数:0
运行
复制
package com.renzhikeji.annotation;

import lombok.Data;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * @author 认知科技技术团队
 * 微信公众号:认知科技技术团队
 */
public class ThreadLocalDemo {
    public static void main(String[] args) throws InterruptedException {
        LoginUserInfos.setUserId(88L);
        System.out.println(LoginUserInfos.getUserId());

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(LoginUserInfos.getUserId());
            }
        });

        thread.start();
        thread.join();
    }

    @Data
    public static final class LoginUserInfos {
        private static final String USER_ID = "userId";
        private static final ThreadLocal<Map<String, Object>> THREAD_LOCAL = new ThreadLocal<>() {
            @Override
            protected Map<String, Object> initialValue() {
                return new HashMap<>();
            }
        };

        public static void setUserId(Long userId) {
            Map<String, Object> map = THREAD_LOCAL.get();
            map.put(USER_ID, userId);
        }

        public static Long getUserId() {
            Object userId = THREAD_LOCAL.get().get(USER_ID);
            //不设置默认值,还可能利用NPE异常发现问题喔
            return (Long) Optional.ofNullable(userId).orElse(0L);
        }
    }
}

源码 https://gitee.com/whoamiy/demo/blame/threadlocal/java-demo/src/main/java/com/renzhikeji/annotation/ThreadLocalDemo.java

第21行输出的肯定是0值,没有获取到真正的用户信息。

小结


java中的ThreadLocal使用,在项目中及框架中的出现很普遍。看过ThreadLocal源码的同学都知道这个是与线程绑定的变量,很容易造成内存泄露,但也会造成信息传递的丢失

我们可以使用maven规则插件,检测ThreadLocal变量及包装此变量的类,在代码中的使用限制。

代码语言:javascript
代码运行次数:0
运行
复制
Thread、
ThreadPoolExecutor、
ScheduledThreadPoolExecutor、
java.util.stream.BaseStream#parallel、
java.util.Collection#parallelStream、
java.util.concurrent.CompletableFuture#supplyAsync(java.util.function.Supplier<U>, java.util.concurrent.Executor)、
java.util.concurrent.CompletableFuture#supplyAsync(java.util.function.Supplier<U>)、
ForkJoinPool、
java.util.concurrent.Executors里方法返回的线程池

等等及框架实现的线程池类我们都必须检测到。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-04-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 认知科技技术团队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档