这两天一直在查无线app一个诡异的问题,表象是stg的接口返回数据,和线上接口的返回数据不一致。

1、初步判断:有缓存,查看代码后发现缓存时间直邮6分钟,而且同一个接口,其他调用方的返回数据,stg和线上是保持一致的。

2、确认版本后,把线上版本和stg环境的版本号,进行多次check,发现版本是一致的。

3、线上和stg接口的返回数据,来源于我依赖的接口,现在接口stg和线上是不一致,而不是一个有数据一个没数据,判断是调用了不同的接口。了解下来接口会根据不同的版本号返回不同的数据,所以判断有版本控制的appClientVersion这个字段传的不对,安装最新的app包,debug我们的stg环境发现版本是4.0.3没有传错。在各种解释不通的情况下,我只好加上日志,把输入输出打出来。

上线后查看日志发现:我的屌丝android手机居然变成了iphone,版本号也是4.0.1,起初怀疑无线版本号不对,连上Fiddler,并切换线上和stg环境,发现请求的clientInfo没有错,的确是android ,4.0.3的版本,那问题肯定是venus到我们的服务再到我们调用服务之前clientInfo被改动了。查看代码发现,clientInfo信息是从ThreadLocal里面拿的。。。原来拿的是别的线程的内容,怪不得连屌丝机都能升级成高富帅。这就可以解释为什么stg永远好的,线上有问题,因为stg测试的全是4.0.3版本的发布包测的。

我们的版本控制是控制interfaceVersion来控制的,再拿到ThreadLocal里面的内容的时候,我们重新赋值了,所以,这个参数没有问题,而appClientVersion和ClientSystem都没有重新赋值,拿到别的线程的内容后就变成了我们所依赖的接口的老版本,所以返回了不同的数据。

ThreadLocal可以为当前线程保存局部变量,而InheritableThreadLocal则可以在创建子线程的时候将父线程的局部变量传递到子线程中。

如果使用了线程池(如Executor),那么即使即使父线程已经结束,子线程依然存在并被池化。这样,线程池中的线程在下一次请求被执行的时候,ThreadLocal对象的get()方法返回的将不是当前线程中设定的变量,因为池中的“子线程”根本不是当前线程创建的,当前线程设定的ThreadLocal变量也就无法传递给线程池中的线程。

  1. import java.util.concurrent.Executor;
  2. importjava.util.concurrent.Executors;
  3. public classThreadLocalTest {
  4. private staticThreadLocal<String> vLocal = newThreadLocal<String>();
  5. public static voidmain(String[] args) {
  6. Executorexecutor = Executors.newFixedThreadPool(2);
  7. // 模拟10个请求
  8. for (int i =0; i < 10; i++) {
  9. final int flag= i;
  10. executor.execute(new Runnable() {
  11. @Override
  12. public voidrun() {
  13. //                   vLocal.set(null);
  14. //模拟某一线程改变了ThreadLocal的值
  15. if (flag == 1) {
  16. vLocal.set("set:test");
  17. }
  18. System.out.println(Thread.currentThread().getName()+ ":" + vLocal.get());
  19. }
  20. });
  21. }
  22. }
  23. }

pool-1-thread-1:null

pool-1-thread-2:set:test

pool-1-thread-1:null

pool-1-thread-2:set:test

pool-1-thread-1:null

pool-1-thread-2:set:test

pool-1-thread-1:null

pool-1-thread-2:set:test

pool-1-thread-1:null
pool-1-thread-2:set:test

因此,必须将外部线程中的ThreadLocal变量显式地传递给线程池中的线程,或者每个请求来的时候先threadLocal.set(null)。

threadLocal遇上线程池导致局部变量变化的更多相关文章

  1. ThreadLocal 遇上线程池的问题及解决办法

    ThreadLocal 称为线程本地存储,它为每一个使用它的线程提供一个其值(value)的副本.可以将 ThreadLocal<T> 理解成 Map<Thread, T>,即 ...

  2. 源码角度分析-newFixedThreadPool线程池导致的内存飙升问题

    前言 使用无界队列的线程池会导致内存飙升吗?面试官经常会问这个问题,本文将基于源码,去分析newFixedThreadPool线程池导致的内存飙升问题,希望能加深大家的理解. (想自学习编程的小伙伴请 ...

  3. ThreadPoolExecutor使用和思考(上)-线程池大小设置与BlockingQueue的三种实现区别

    工作中多处接触到了ThreadPoolExecutor.趁着现在还算空,学习总结一下. 前记: jdk官方文档(javadoc)是学习的最好,最权威的参考. 文章分上中下.上篇中主要介绍ThreadP ...

  4. hreadPoolExecutor使用和思考(上)-线程池大小设置与BlockingQueue的三种实现区别

    阅读更多 工作中多处接触到了ThreadPoolExecutor.趁着现在还算空,学习总结一下. 前记: jdk官方文档(javadoc)是学习的最好,最权威的参考. 文章分上中下.上篇中主要介绍Th ...

  5. Python ThreadPoolExecutor 线程池导致内存暴涨

    背景 在有200W的任务需要取抓取的时候,目前采用的是线程池去抓取,最终导致内存暴涨. 原因 Threadpoolexcutor默认使用的是无界队列,如果消费任务的速度低于生产任务,那么会把生产任务无 ...

  6. 在使用线程池时应特别注意对ThreadLocal的使用

    使用ThreadLocal并且有线程池时要特别注意,ThreadLocal是以线程为key的,而线程池里面的线程是会被重新利用的,所以如果有使用线程池并且使用ThreadLocal来保存状态信息时要特 ...

  7. 009-ThreadPoolExecutor运转机制详解,线程池使用1-newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor、newScheduledThreadPool

    一.ThreadPoolExecutor理解 为什么要用线程池: 1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务. 2.可以根据系统的承受能力,调整线程池中工作线线程的数 ...

  8. 深入理解Java之线程池(爱奇艺面试)

    爱奇艺的面试官问 (1) 线程池是如何关闭的 (2) 如何确定线程池的数量 一.线程池销毁,停止线程池 ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown() ...

  9. 线程池之ThreadPoolExecutor线程池源码分析笔记

    1.线程池的作用 一方面当执行大量异步任务时候线程池能够提供较好的性能,在不使用线程池的时候,每当需要执行异步任务时候是直接 new 一线程进行运行,而线程的创建和销毁是需要开销的.使用线程池时候,线 ...

随机推荐

  1. XAMPP打不开Apache服务的解决办法

    XAMPP打不开Apache服务的解决办法 不用修改设置,应该是80端口被占用了,直接先IIS的网站给停了就OK

  2. jquery文档内容的获取和设置

  3. 利用StringBuffer来替换内容

    package com.test.java; public class StringBufferTest { public static void main(String[] args) { Stri ...

  4. C#中的explicit和implicit了解一下吧

    今天在研究公司项目框架的时候看到了下面的用法,public static implicit operator JsonData(int data);.貌似很久没用过这种隐式转换的写法了,因此重新温习一 ...

  5. .NET 5 - 下一代.NET

    不知不觉中微软已经计划推出了下一代的.NET了,我们先来看一下新的.NET有包含什么 What's new in .NET 5? .NET 5将会引入新的APIs,运行时功能和新的语言特色. 在运行时 ...

  6. phpstrom安装bootstrap3插件

    1.步骤 File > > Settings > >Plugins > > 搜索bootstrap 3 然后点击 Browse repositories 就会有一个 ...

  7. POJ3734【状压枚举】

    题意: 给你两个01矩阵,去掉矩阵B的某些行和某些列,问处理后的矩阵B能否变成矩阵A: 思路: 数据较小,状压枚举B矩阵列的数量=A矩阵列的数量时的状态,然后搞定了列,贪心判断B矩阵的行就好了: #i ...

  8. spring boot 使用Schedule创建轻量级定时任务

    Scheduled SpringBoot配置定时任务可以直接使用自带的Scheduled,这相当于一个轻量级的Quartz,它可以让我们直接使用注解来完成定时任务的配置. Scheduled调度时间设 ...

  9. builtin_shaders-5.3.4f1学习-Unlit/Texture

    // Unlit shader. Simplest possible textured shader. // - no lighting // - no lightmap support // - n ...

  10. windows7任务管理器内存相关列详细解释

    内存 - 工作集:私人工作集中的内存数量与进程正在使用且可以由其他进程共享的内存数量的总和.  内存 - 峰值工作集:进程所使用的工作集内存的最大数量.  内存 - 工作集增量:进程所使用的工作集内存 ...