InheritableThreadLocal线程复用取值错误问题

4 篇文章 0 订阅
2 篇文章 0 订阅

前言

  • 随着互联网的进步,互联网企业对安全越来越重视,以前对于客户的信息都是通过前端传递后端接受来完成,可这样就非常不安全但凡懂点结束的人都可以随意获取到用户信息
  • 于是大家就开始使用token来进行传递数据,后端拿到token进行解析然后取到里面需要的值进行操作但是只要有用到的地方就需要解析很麻烦
  • 为了方便就演化成将token解析后存入InheritableThreadLocal当前线程如果需要的话就直接通过本地线程就可以直接拿到数据很方便

问题

  • 线程池里面的线程对于本地本地线程里面的数据会一直保留

问题复现
示例1

public static void main(String[] args) {
    ThreadPoolExecutor executor = new ThreadPoolExecutor(3,3,0, TimeUnit.SECONDS,
             new ArrayBlockingQueue<>(20),new ThreadPoolExecutor.AbortPolicy());
    InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
    AtomicBoolean flag = new AtomicBoolean(true);
    for (int i = 0; i < 10; i++) {
        executor.execute(() -> {
            if(flag.get()){
                threadLocal.set("张三吃豆芽" + Thread.currentThread().getName());
                flag.set(false);
            }
           System.out.println(Thread.currentThread().getName() + "==" + threadLocal.get());
       });
    }
}
打印结果:
pool-1-thread-1==张三吃豆芽pool-1-thread-1
pool-1-thread-2==null
pool-1-thread-3==null
pool-1-thread-2==null
pool-1-thread-1==张三吃豆芽pool-1-thread-1
pool-1-thread-3==null
pool-1-thread-2==null
pool-1-thread-3==null
pool-1-thread-1==张三吃豆芽pool-1-thread-1
pool-1-thread-2==null
  1. 创建一个核心数3的线程池
  2. 创建一个主线程的本地线程对象
  3. 循环十个任务只有第一次进来的时候给本地线程塞值,因为InheritableThreadLocal的特性这里是可以获取到主线程的本地线程对象且可以塞值
  4. 通过打印结果我们可以发现线程1使用了三次且每次都有其他任务里面塞的数据

有同学会说,我使用完把这个线程里面的本地线程数据删除不就行了,确实这确实可以 但是我们一般解析token塞入数据是不是都是在主线程做的操作 然后子线程再去复制主线程的本地线程数据我们继续往下看

示例2

public static void main(String[] args) {
    ThreadPoolExecutor executor = new ThreadPoolExecutor(3,3,0, TimeUnit.SECONDS,
             new ArrayBlockingQueue<>(20),new ThreadPoolExecutor.AbortPolicy());
    InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
    threadLocal.set("张三吃豆芽" + Thread.currentThread().getName());
    for (int i = 0; i < 10; i++) {
        executor.execute(() -> {
          System.out.println(Thread.currentThread().getName() + "==" + threadLocal.get());
       });
    }
    threadLocal.remove();
    for (int i = 0; i < 10; i++) {
        executor.execute(() -> {
          System.out.println(Thread.currentThread().getName() + "==" + threadLocal.get());
    });
}
打印结果:
pool-1-thread-1==张三吃豆芽main
pool-1-thread-3==张三吃豆芽main
pool-1-thread-2==张三吃豆芽main
pool-1-thread-3==张三吃豆芽main
pool-1-thread-3==张三吃豆芽main
pool-1-thread-3==张三吃豆芽main
pool-1-thread-3==张三吃豆芽main
pool-1-thread-3==张三吃豆芽main
pool-1-thread-1==张三吃豆芽main
pool-1-thread-3==张三吃豆芽main
pool-1-thread-2==张三吃豆芽main
pool-1-thread-3==张三吃豆芽main
pool-1-thread-3==张三吃豆芽main
pool-1-thread-3==张三吃豆芽main
pool-1-thread-3==张三吃豆芽main
pool-1-thread-3==张三吃豆芽main
pool-1-thread-3==张三吃豆芽main
pool-1-thread-1==张三吃豆芽main
pool-1-thread-3==张三吃豆芽main
pool-1-thread-2==张三吃豆芽main
  1. 这段代码和上段代码不同的是我们在本地线程塞值的时机改成了在主线程上面
  2. 之后跑十个任务,这时候线程池里面的三个线程都复制了主线程里面的本地线程数据(这里的复制不是指针是new出来的本地线程对象)
  3. 删除主线程的本地线程数据
  4. 再次使用线程池里面的线程,发现数据还是在(也证明了子线程是深拷贝主线程的本地线程数据)

问题原因

  • 不难看出,因为我们使用的是InheritableThreadLocal,子线程可以复制主线程本地线程的数据导致子线程的InheritableThreadLocal字段也有数据,如果使用的是线程池那么当前线程里面的数据就可能被误用

解决办法

目前主线程只能通过当前线程结束的时候进行remove()操作删除本地线程里面的数据,但是如果使用了线程池里面的数据那除非是重新塞值,其他我也想不到好的办法像我们平时使用的tomcat线程池里面如果进行了复制请求进来会重新塞值这个不会有什么问题,但是如果这时候有定时任务或者事件之类的进来不会重新塞值就可能会引发这样的问题

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Python中,线程复用是指在多个任务之间共享线程,以提高系统资源的利用率。在给定的线程池中,可以创建一定数量的线程,并将这些线程分配给需要执行的任务。通过复用线程,可以减少线程创建和销毁的开销,提高程序的性能。 在给定的代码示例中,通过创建线程池并使用全局变量来控制线程的操作,实现了线程复用的效果。在示例中,使用线程池并创建了5个线程,这些线程会执行calc函数中的任务。在示例中,通过使用锁对全局变量进行保护,避免了多个线程对同一变量进行操作的问题。而在示例中,通过传递参数的方式创建了254个线程,并使用了全局变量ips来存储执行结果。 总结来说,线程复用是通过创建线程池并共享线程来提高系统资源的利用率。在Python中,可以通过使用全局变量和锁来控制线程的操作,实现线程复用的效果。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Python多线程代码执行重复](https://blog.csdn.net/qq_59369367/article/details/124350865)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值