不規范使用ThreadLocal導致的bug,說多了都是淚
ThreadLocal一般用于線程間的數據隔離,通過將數據緩存在ThreadLocal中,可以極大的提升性能。但是,如果錯誤的使用Threadlocal,可能會引起不可預期的bug,以及造成內存泄露。
因為線程重用導致的信息錯亂的bug
有時我們會在一個接口中緩存某些數據到ThreadLocal中,但是我們要意識到,處理請求的這些線程是由tomcat提供的,而tomcat提供的線程都是配置在一個線程池中的。
也就是說,線程是可能被重用的,如果線程一旦被重用,而ThreadLocal的數據沒有及時重置,就會導致數據被混亂使用。
以下方的接口為例,先獲取當前線程中保存的數據信息,將參數中的name保存到ThreadLocal中以后,再獲取一次。
為了盡快復現線程重用導致的問題,我們將
servlet.tomcat.threads.max設置為1,這樣每次請求使用的都是同一個線程。
第一次請求接口,數據看起來很正常:
但是第二次請求接口時,可以看到線程仍然是http-nio-8080-exec-1,但是before卻打印出了第一次請求的參數test。
這就是因為沒有及時重置ThreadLocal導致的數據錯誤。
正確使用的姿勢
修正的辦法就是處理完接口之后要及時清理ThreadLocal。
更優雅的處理方式
可能也有的朋友會說,每次都要使用try finally處理線程數據,未免也太麻煩了。其實,我們可以使用攔截器或者過濾器自動幫我們完成數據的初始化以及清理工作。
最后
我們在寫業務代碼時,正確的理解線程的全生命周期以及執行原理,對我們提升代碼的健壯性其實很有幫助。有時我們覺得底層原理很枯燥乏味,開發業務就是寫增刪改查,多線程用的也很少,但我們只是沒有意識到,我們的代碼一直跑在tomcat提供的線程池中,本身就是一個多線程的環境。
除了tomcat的線程池,我們自定義的線程池其實也會有這個問題,大家可以看看自己的業務代碼是否踩過這個坑。