博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ThreadLocal对象使用过程中容易陷入的坑
阅读量:5922 次
发布时间:2019-06-19

本文共 1139 字,大约阅读时间需要 3 分钟。

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、保证在每个请求结束后清空线程变量。

转载地址:http://ijivx.baihongyu.com/

你可能感兴趣的文章
Java 调用 Javascript 函数的范例
查看>>
拍照 和 选择相册 设置背景图片
查看>>
通知中心
查看>>
PHP-php-fpm占用系统资源分析
查看>>
CentOS 7 关闭防火墙和SELinux
查看>>
【转】将Vim改造为强大的IDE—Vim集成Ctags/Taglist/Cscope/Winmanager/NERDTree/OmniCppComplete(有图有真相)...
查看>>
S5P4418 uboot 分析
查看>>
Eclipse格式化代码换行、删除空行
查看>>
uva 11324 The Largest Clique(图论-tarjan,动态规划)
查看>>
Swift - 微调器或叫步进器(UIStepper)的用法
查看>>
微信公众平台开发问答
查看>>
Project Euler 78:Coin partitions
查看>>
Android调用Webservice发送文件
查看>>
HDU--3081--Marriage Match II--最大匹配,匈牙利算法
查看>>
至少有6000个程序员比我勤奋
查看>>
SOLID原则
查看>>
Sublime必备插件
查看>>
MyBatis入门(五)---延时加载、缓存
查看>>
js原生设计模式——2面向对象编程之闭包2
查看>>
Going Home(最大匹配km算法)
查看>>