这两天一直在查无线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. 【Data Structure & Algorithm】二叉树中和为某值的所有路径

    二叉树中和为某值的所有路径 题目:输入一个整数和一个二叉树,从树的根节点开始往下访问一直到叶节点所经过的所有节点形成一条路径.打印出和与输入整数相等的所有路径. 例如输入整数22和如下二叉树: 10 ...

  2. ASP.NET Core MVC 2.x 全面教程_ASP.NET Core MVC 27. CICD Azure DevOps

    VSTS做持续集成 后来改名叫做Azure Deveps https://azure.microsoft.com/zh-cn/services/devops/ 这是中文的地址 创建一个项目 名称.描述 ...

  3. bugfree-解决方案的意思

    BugFree的7种解决方案各自的含义: By Design - 就是这么设计的,无效的Bug Duplicate - 这个问题别人已经发现了,重复的Bug External - 是个外部因素(比如浏 ...

  4. __doPostBack方法解析 __VIEWSTATE __EVENTTARGET __doPostBack __EVENTARGUMENT

    关于这个的另一篇博客:http://www.cnblogs.com/Silicon-Fado/archive/2009/04/21/1440437.html __VIEWSTATE:页面状态信息在客户 ...

  5. 7 二分搜索树的原理与Java源码实现

    1 折半查找法 了解二叉查找树之前,先来看看折半查找法,也叫二分查找法 在一个有序的整数数组中(假如是从小到大排序的),如果查找某个元素,返回元素的索引. 如下: int[] arr = new in ...

  6. phpstudy的mysql版本升级至5.7

    phpstudy安装的mysql版本一般都是5.5或5.4的,但是有时候做项目又必须用到mysql5.7版本,所以我们现在来看一下如何在phpstudy的环境下将mysql版本升级至5.7 温馨提醒: ...

  7. lightoj 1078【同余定理】

    题意: 给你一个n和一个数 digit ,问你最少需要多少个 digit 使得整除于n; 思路: 同余定理(a+b)%n=(a%n+b%n)%n; (m%n+m%n*10+m%n*100+m%n*10 ...

  8. FileReader文件读取API

    :用来把文件读入内存,并且读取文件中的数据.FileReader接口提供了一个异步API,使用该API可以在浏览器主线程中异步访问文件系统,读取文件中的数据. 1.FileReader接口的方法 Fi ...

  9. 3DMAX 1快捷键及常用操作

    开启,关闭快捷键 ,使用快捷键时要按下这个按钮 快捷键查看与修改 自定义-自定义用户界面(cutomize user interface):设置和查看快捷键 位置变换 Z: 复位---物体被移动飞了的 ...

  10. 百度搜索:有关Baiduspider的10个问题

    猫宁!!! 参考链接: http://help.baidu.com/question?prod_id=99&class=476&id=2996 https://ziyuan.baidu ...