ThreadLocal对象帮助咱们管理线程内的对象,保证对象在线程之间是相互隔离的。html
今天碰到的坑是这样的:web
index01.html页面加载的时候会发送一次a请求,而后点击附件上传的时候会发送上传请求b,上传成功后会发送下载请求c,浏览器
其中a请求会通过interceptor01拦截器,interceptor01内部会将a请求传递的module_name参数存入本地线程变量,b请求不会通过拦截器,c请求会通过拦截器,可是不会传递module_name,这时线程变量会存入一个空的module_name。tomcat
具体现象是这样的,进入index01.html页面,第一次点击上传附件后,下载附件能够成功,可是只要不关闭浏览器从新打开页面,后续点击的全部上传请求都不能成功,而且报module_name为空的错,module_name是从本地线程变量中获取的。线程
所以怀疑是否是module_name参数没有存入本地线程,通过几番查询发现,b请求不通过拦截器,因此b请求中拿不到module_name是正常的,可是奇怪的是,b请求居然可以拿到本地线程变量中的其它的属性值,真实百思不得其解,由于能够肯定,线程变量只有在拦截器interceptor01中才会初始化并赋值!htm
让咱们再梳理一下,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个缘由:
一、http1.1协议中的keep-alive是默认开启的,同一个会话中,有限的请求是共用一个长链接的。
二、tomcat默认使用线程池,因此一个线程的生命周期不能对等于一个请求的生命周期,线程池中的线程是能够被复用的。
解决方案:
一、保证每次都用新的值覆盖线程变量;
二、保证在每一个请求结束后清空线程变量。