ThreadLocal可能踩到的坑

我是小鳴發表於2018-09-20

背景

  • 首先ThreadLocal 不做太多的介紹,在多執行緒場景下有著廣泛的應用。
  • 最近在實現OneLog方法級日誌的時候,使用了 ThreadLocal 作為快取和計數器,在除錯過程中,發現有一些場景會出現資料錯亂和記憶體溢位的問題。

詳情

  • OneLog方法級日誌使用 ThreadLocal 有兩個場景,一個是將方法入參資訊快取起來,然後在方法結束之後與返回結果進行組裝。另一個是作為計數器使用,保證迴圈方法的場景下,不會列印太多的日誌。
  • 實際使用的時候發現了問題,ThreadLocal 物件的生命週期是依賴於執行緒的,那麼對於一個web應用來說,tomcat的執行緒是迴圈使用的,也就是說在web應用中,主執行緒下 ThreadLocal 物件永遠不會被回收!
  • 下面是問題復現示例程式碼:
@Controller
public class TestThreadLocalController {
    //計數器
    private final ThreadLocal<Integer> counter = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };
    @RequestMapping("/testThreadLocal")
    public @ResponseBody  Integer testThreadLocal() {
        doSomething();
        return counter.get();
    }
    private void doSomething() {
        counter.set(counter.get()+1);
    }
}
  • 執行多次web介面呼叫,當tomcat執行緒出現重複使用的時候,即可看到返回值大於1的情況:
    ​​image.png

解決方案

  • 線上程池(包括web主執行緒)中使用 ThreadLocal 的時候,一定要考慮到 ThreadLocal 物件的生命週期是跟隨執行緒的,會隨著執行緒池的迴圈而一直存在。
  • 那麼在這種場景下,使用 ThreadLocal 就要注意這幾點:

    • Map、List、Set等集合物件,要考慮資料是否會一直增加,如果一直增加而不移除的話就會造成記憶體溢位。
    • 用作計數器的話,使用之後要清0,不然計數的數值會隨著執行緒一直儲存下來。


相關文章