zoukankan      html  css  js  c++  java
  • ThreadLocal对象使用过程中容易陷入的坑

    ThreadLocal对象帮助我们管理线程内的对象,保证对象在线程之间是相互隔离的。

    今天碰到的坑是这样的:

    index01.html页面加载的时候会发送一次a请求,然后点击附件上传的时候会发送上传请求b,上传成功后会发送下载请求c,

    其中a请求会经过interceptor01拦截器,interceptor01内部会将a请求传递的module_name参数存入本地线程变量,b请求不会经过拦截器,c请求会经过拦截器,但是不会传递module_name,这时线程变量会存入一个空的module_name。

    具体现象是这样的,进入index01.html页面,第一次点击上传附件后,下载附件可以成功,但是只要不关闭浏览器重新打开页面,后续点击的所有上传请求都不能成功,并且报module_name为空的错,module_name是从本地线程变量中获取的。

    因此怀疑是不是module_name参数没有存入本地线程,经过几番查询发现,b请求不经过拦截器,所以b请求中拿不到module_name是正常的,但是奇怪的是,b请求竟然能够拿到本地线程变量中的其它的属性值,真实百思不得其解,因为可以确定,线程变量只有在拦截器interceptor01中才会初始化并赋值!

    让我们再梳理一下,b请求没有经过拦截器,那么本地线程变量就没有初始化,但是在b请求中取本地线程变量的时候,竟然能取到,只是唯独module_name取不到。

    好吧,可以肯定线程变量只能在本线程中获取到,那么只有一个解释,那就是b请求与别的请求共用了同一个线程!换句话说,并不是每一个请求,web容器都会使用不同的线程来处理。为了证实这一想法,我将a、b、c请求的Threadid打印出来比对,发现果然是一样的。

    这就可以解释上述的问题了:

    当我们第一次进入index01.html页面,a请求会经过拦截器,然后初始化线程变量,存入module_name;

    接着b请求不会经过拦截器,但是由于和a请求使用的是同一个线程,所以能够正常取出module_name,并成功上传;

    接着c请求会经过拦截器,但是不会传递module_name,所以把线程中的module_name就置空了;

    最后当我们再次上传发送b请求的时候,线程中就没有module_name了。

    下面说说为什么3个请求会共用一个线程,2个原因:

    1、http1.1协议中的keep-alive是默认开启的,同一个会话中,有限的请求是共用一个长连接的。

    2、tomcat默认使用线程池,所以一个线程的生命周期不能对等于一个请求的生命周期,线程池中的线程是可以被复用的。

    解决方案:

    1、保证每次都用新的值覆盖线程变量;

    2、保证在每个请求结束后清空线程变量。

  • 相关阅读:
    portal商品展示功能逻辑
    springboot读取自己定义的配置文件的方式以及使用joda_time来处理时间日期
    文件上传与下载总结
    swagger2的常用注解,传递参数的注意使用方法
    小知识回顾
    springboot如何集成mybatis的pagehelper分页插件
    MySQL基础之第8章 视图
    MySQL基础之第7章 索引
    MySQL基础之第6章 创建、修改和删除表 .
    MySQL基础之第5章 操作数据库
  • 原文地址:https://www.cnblogs.com/firstdream/p/7886628.html
Copyright © 2011-2022 走看看