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默認使用線程池,因此一個線程的生命週期不能對等於一個請求的生命週期,線程池中的線程是能夠被複用的。
解決方案:
一、保證每次都用新的值覆蓋線程變量;
二、保證在每一個請求結束後清空線程變量。