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、保證在每個請求結束后清空線程變量。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM