在 dubbo 中使用 Threadlocal 的相關問題

原由

最近進入項目組開發,偶爾翻閱別人代碼時,看到以下注釋。 html

俏皮註釋
注:該ThreadLocalUtil 就是內部使用 ThreadLocal 內部靜態變量。

剛看到時徹底不解,不知道其爲何這麼說。當同事跟我解釋緣由,豁然開朗。細想,咱們在工做使用中,若是不注意很容易就忽略這個問題。 在解釋這個問題先,咱們先來查看 ThreadLocal。java

ThreadLocal

ThreadLocal 提供線程局部(thread-local)變量,爲每一個線程建立單獨的變量副本,這樣在多線程環境的下,因爲每一個線程都有單獨的變量,不會由於變量共享致使的併發問題。固然相關問題我能夠們使用同步機制解決該問題。git

同步機制跟 ThreadLocal 區別github

使用同步機制,多線程環境共享同一變量,這須要咱們在使用時顯示加鎖,保證變量在同一時間只有一個線程能使用,至關於採用時間策略換取線程安全。web

使用 ThreadLocal,每一個線程擁有本身獨立變量副本,至關於採用空間策略換取線程安全。spring

咱們從代碼查看下兩種方式的區別。如今假如咱們須要當前一個時間工具類,以下:apache

private final static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static String getDateStr(Date date) {
        return format.format(date);
    }
複製代碼

查看上面 Demo,咱們很容易能夠看出上面方法因爲是 SimpleDateFormat 不是線程安全,從而致使 getDateStr 方法非線程安全。安全

如今咱們使用同步對其改造。多線程

private final static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static synchronized String getDateStrSync(Date date) {
        return format.format(date);
    }
複製代碼

這樣使用顯示加鎖,避免多線程環境下線程安全。可是這種方式可能在高併發狀況影響效率。併發

下面使用 ThreadLocal 對其改造。

private final static ThreadLocal<SimpleDateFormat> formatLocal = new ThreadLocal<SimpleDateFormat>() {
        protected synchronized SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };


    public static String getDateByLocal(Date date){
        return formatLocal.get().format(date);
    }
複製代碼

使用 ThreadLocal,將共享變量變成獨享變量,保證線程安全。可是該種方案增長建立對象的開銷。

綜上,如何選擇上述兩種方式,須要結合當前業務方式選擇。

講完 ThreadLocal,咱們來看下的 dubbo 的線程模型。

dubbo 線程模型

dubbo 默認採用單一長鏈接加線程池方式處理調用。

dubbo 調用圖

默認採起 Dispatcher=all 的分發策略,全部消息都派發到線程池,包括請求,響應,鏈接事件,斷開事件,心跳等。線程池在缺省配置爲固定大小線程池,啓動時創建線程,不關閉,一直持有。默認爲100個線程。

分析在 Dubbo 中使用 ThreadLocal

在 Dubbo 中使用 ThreadLocal ,若是採用默認的設置,每次 Dubbo 調用結束,Dubbo 處理響應線程並不會被銷燬, 而是歸還到線程池中。而從 ThreadLocal 源碼能夠看出,每次咱們設置的值其實會存在位於 Thread 中 ThreadLocalMap 變量中。

Thread與ThreadLocal對象之間的引用關係圖

這就致使,下次若是 Dubbo 處理響應剛好繼續使用到這個線程,該線程就能調用到上次響應中設置在 ThreadLocal 設置的值。這就引發內存泄露,可能還會致使業務上異常。其實並不止在 Dubbo 中,該案例還會發生在 web項目中,只要相關使用線程池的,都有可能發生。

Threadlocal 總結

使用 Threadlocal,咱們須要注意幾點:

  1. 使用 Threadlocal 須要跟使用相關流同樣,須要最後顯示調用其 remove 方法,清除其設置的值,防止引發內存泄露。
  2. 不能什麼傳參都使用 Threadlocal 在方法中上下傳遞,這樣就會引發隱形耦合,並且相關代碼並很差維護。
  3. 高併發中,咱們可使用 Threadlocal 代替同步鎖,提升相關性能。

參考連接

  1. 【死磕Java併發】—–深刻分析ThreadLocal
  2. 聊一聊Spring中的線程安全性
  3. 優雅的使用 ThreadLocal 傳遞參數
  4. www.baeldung.com/java-thread…
  5. www.cnblogs.com/youzhibing/…
  6. dubbo 線程模型
  7. How to shoot yourself in foot with ThreadLocals

若是以爲好的話,請幫做者點個讚唄~ 謝謝

喜歡本文的讀者們,歡迎長按關注訂閱號程序通事~讓我與你分享程序那些事。

相關文章
相關標籤/搜索