前言
在閱讀前,您需要對ThreadLocal用法有一定了解。如果不了解,請參考ThreadLocal用法相關(guān)文章。
子線程如何獲取主線程的ThreadLocal里的數(shù)據(jù)呢?
這個需求或許也很常見,最開始,我們可能會想到InheritableThreadLocal,這是由Jdk為我們提供,他是ThreadLocal的子類,使用方法和ThreadLocal完全相同。
但是這里有幾個坑:
1、手動通過線程池創(chuàng)建線程可能會造成get值為null。
2、項目中我們往往會使用線程池,如果主線程使用的是緩存線程池(比如SpringMvc),線程會復(fù)用,當(dāng)線程執(zhí)行完畢后本次操作后,再次執(zhí)行新的任務(wù)時候,ThreadLocal內(nèi)部數(shù)據(jù)并沒有被清除。
3、ThreadLocal父子線程之間數(shù)據(jù)拷貝默認(rèn)是淺拷貝,這也就意味如果我們多個線程可能會引用同一個內(nèi)存地址,造成多個線程訪問一個對象,輕者會造成線程不安全,重者甚至?xí)hreadLocal數(shù)據(jù)被修改成非預(yù)期結(jié)果。
終級解決方案
這些坑本人親身體驗過,血的教訓(xùn)總結(jié)出來,那么如何很好的解決這些問題呢?這邊給出一個終級解決方案,使用阿里Transmittable ThreadLocal,可以很好的解決上面這些問題。
具體使用方法可以參見文檔:https://github.com/alibaba/transmittable-thread-local
如果使用后發(fā)現(xiàn)在緩存線程池下仍然會出現(xiàn)ThreadLocal沒有被清理干凈的問題,可可嘗試重寫TransmittableThreadLocal afterExecute方法,在此方法中清除線程數(shù)據(jù)。
那么通過阿里的TransmittableThreadLocal 如何解決淺拷貝問題呢?這里它提供了一個copy方法,TransmittableThreadLocal 在進行對象拷貝的時候會調(diào)用copy方法,我們只需要重寫這個方法,讓他變量深復(fù)制即可。
最后,建議大家不要輕意使用ThreadLocal,能不用盡量不要去使用,一旦在緩存線程池下如果數(shù)據(jù)沒有清理干凈會很麻煩,不方便問題排查,而且ThreadLocal有一定情況下會有造成內(nèi)存泄露的風(fēng)險,一定要慎用!慎用!慎用!但并不意味著禁用!