并發(fā)編程-Threadlocal

上一篇 <<<Fork/Join框架
下一篇 >>>Disruptor框架


Threadlocal: 各個線程獨有的局部變量,相互之間不受影響诚纸。
它主要有四個方法initialValue()瞬沦、get()晶乔、set()和remove(),底層采用了map集合形式進行存放,key為當前線程ID砂客。

ThreadLocal的優(yōu)勢

1.多線程的情況下臭家,每個線程之間相互隔離
2.傳遞參數(shù)

ThreadLocal的應用場景

  • Spring中使用的request對象、session對象
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
        .getRequestAttributes()).getRequest();

private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
      new NamedThreadLocal<>("Request attributes");

private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
      new NamedInheritableThreadLocal<>("Request context");
  • Spring中使用的事務傳播行為:事務標識房午、jdbc鏈接等都放在ThreadLocal中

ThreadLocal內(nèi)部結構

1.Thread類之中有一個屬性threadLocals矿辽,內(nèi)部有一個Entry數(shù)組屬性,類似于map郭厌,不同的是用set方法袋倔,非put。
2.Entry key存放的是ThreadLocal對象 折柠,value存放的是我們存儲的對象宾娜。
3.set方法調(diào)用時,判斷key是否存在扇售,如果為空則創(chuàng)建前塔。當前threadlocal為key,value存放我們?nèi)氲闹怠?br> 存在則更新value值承冰。

ThreadLocal內(nèi)存泄露問題

threadlocal里面的Entry extends WeakReference(弱引用)
弱引用的特點是只能存在于下一次gc之前华弓,發(fā)生minorgc majorgc就會被回收,造成key變?yōu)榭绽梗瑅alue還會被棧使用寂屏,也就造成了內(nèi)存泄露問題。

ThreadLocal自身的清理過程

在ThreadLocal的get(),set(),remove()的時候都會清除線程ThreadLocalMap里所有key為null的value。

image.png

如何避免內(nèi)存泄露

    1. 可以自己調(diào)用remove方法將不要的數(shù)據(jù)移除避免內(nèi)存泄漏的問題
    1. 每次在做set方法的時候會清除之前 key為null
  • 3.使用java反射機制獲取當前線程對應的ThreadLocalMap 凑保,手動移除

為什么線程中的ThreadLocal采用弱引用而不是強引用

  • 如果key是為強引用:

當我們現(xiàn)在將ThreadLocal 的引用指向為null冈爹,但是每個線程中有自己獨立ThreadLocalMap還一直在繼續(xù)持有該對象涌攻,但是我們ThreadLocal 對象不會被回收欧引,就會發(fā)生ThreadLocal內(nèi)存泄漏的問題。

  • 如果key是為弱引用:

當我們現(xiàn)在將ThreadLocal 的引用指向為null恳谎,Entry 中的key指向為null芝此,但是下次調(diào)用set方法的時候,會根據(jù)判斷如果key空的情況下因痛,直接刪除婚苹,有可能會發(fā)生Entry 發(fā)生內(nèi)存泄漏的問題。

不管是用強引用還是弱引用都是會發(fā)生內(nèi)存泄漏的問題鸵膏。弱引用中不會發(fā)生ThreadLocal內(nèi)存泄漏的問題膊升。
但是最終根本的原因Threadlocal內(nèi)存泄漏的問題,產(chǎn)生于ThreadLocalMap與我們當前線程的生命周期一樣谭企,如果沒有手動的刪除的情況下廓译,就有可能會發(fā)生內(nèi)存泄漏的問題。

為什么線程中的ThreadLocalMap 底層數(shù)組Entry實現(xiàn)

因為threadlocal可能會用多次债查,比如:

ThreadLocal的缺陷

1.父線程不會傳遞給子線程

private static ThreadLocal threadLocal = new ThreadLocal();

public static void main(String[] args) {
    threadLocal.set("123456");
    System.out.println("父線程的值:"+threadLocal.get());
    new Thread(()->{
        System.out.println("子線程的值:"+threadLocal.get());
    }).start();
}

打臃乔:
父線程的值:123456
子線程的值:null

使用InheritableThreadLocal可解決父子傳遞【原理:在調(diào)用init方法時,會將父類的值一一復制放入子類線程中】

private static ThreadLocal threadLocal = new InheritableThreadLocal();
public static void main(String[] args) {
    threadLocal.set("123456");
    System.out.println("父線程的值:"+threadLocal.get());
    new Thread(()->{
        System.out.println("子線程的值:"+threadLocal.get());
    }).start();
}
打禹锿ⅰ:
父線程的值:123456
子線程的值: 123456

2.線程池傳遞問題

使用線程池后征绸,子線程不隨父線程而改變

private static ThreadLocal threadLocal = new InheritableThreadLocal();
private static ExecutorService service = Executors.newFixedThreadPool(1);
public static void main(String[] args) {
    threadLocal.set("123456");
    System.out.println("父線程首次的值:"+threadLocal.get());
    Thread thread = new Thread(() -> {
        System.out.println("子線程的值:" + threadLocal.get());
    });
    service.submit(thread);
    threadLocal.set("7890j");
    System.out.println("父線程修改后的值:"+threadLocal.get());
    service.submit(thread);
}

打印:
父線程首次的值:123456
父線程修改后的值:7890j
子線程的值:123456
子線程的值:123456

使用第三方的TransmittableThreadLocal解決線程池傳遞

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>transmittable-thread-local</artifactId>
    <version>2.10.2</version>
</dependency>

private final static ExecutorService execute = Executors.newFixedThreadPool(1);
private static final ThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();

public static void main(String[] args) throws InterruptedException {
    //第一次設置值為1
    threadLocal.set("1");
    execute.submit(TtlRunnable.get(() ->
            System.out.println(Thread.currentThread().getName()
                    + " 第一次打印ThreadLocal值:" + threadLocal.get())
    ));
    Thread.sleep(1000);
    //第二次設置值為2
    threadLocal.set("2");
    execute.submit(TtlRunnable.get(() ->
            System.out.println(Thread.currentThread().getName()
                    + " 第二次打印ThreadLocal值:" + threadLocal.get())
    ));
    Thread.sleep(1000);
    execute.shutdown();
}
打佣碚肌:
pool-1-thread-1 第一次打印ThreadLocal值:1
pool-1-thread-1 第二次打印ThreadLocal值:2

在我們開發(fā)中管怠,可能要使用hystrix框架,而此框架默認使用線程池隔離缸榄,那么在我們Controller 就無法使用RequestContextHolder來獲取request渤弛。在實際開發(fā)中可以使用信號量隔離來解決這種問題,還可以重寫 hystrix中的HystrixConcurrencyStrategy并重寫wrapCallable 方法

Threadlocal 與Synchronized

1.共同特征:都可以解決線程安全問題
2.Threadlocal 占內(nèi)存碰凶、Synchronized不是很占內(nèi)存
Synchronized 當多個線程在同時共享到同一個全局變量的時候暮芭,只能有一個線程對該變量做修改操作。缺陷:效率非常低 以時間換空間方式
Threadlocal 每個線程中有獨立的緩存的局部變量欲低,以空間換時間的方式提高效率辕宏。


相關文章鏈接:
<<<多線程基礎
<<<線程安全與解決方案
<<<鎖的深入化
<<<鎖的優(yōu)化
<<<Java內(nèi)存模型(JMM)
<<<Volatile解決JMM的可見性問題
<<<Volatile的偽共享和重排序
<<<CAS無鎖模式及ABA問題
<<<Synchronized鎖
<<<Lock鎖
<<<AQS同步器
<<<Condition
<<<CountDownLatch同步計數(shù)器
<<<Semaphore信號量
<<<CyclicBarrier屏障
<<<線程池
<<<并發(fā)隊列
<<<Callable與Future模式
<<<Fork/Join框架
<<<Disruptor框架
<<<如何優(yōu)化多線程總結

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市砾莱,隨后出現(xiàn)的幾起案子瑞筐,更是在濱河造成了極大的恐慌,老刑警劉巖腊瑟,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件聚假,死亡現(xiàn)場離奇詭異块蚌,居然都是意外死亡,警方通過查閱死者的電腦和手機膘格,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門峭范,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瘪贱,你說我怎么就攤上這事纱控。” “怎么了菜秦?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵甜害,是天一觀的道長。 經(jīng)常有香客問我球昨,道長尔店,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任主慰,我火速辦了婚禮嚣州,結果婚禮上,老公的妹妹穿的比我還像新娘河哑。我一直安慰自己避诽,他們只是感情好,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布璃谨。 她就那樣靜靜地躺著沙庐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪佳吞。 梳的紋絲不亂的頭發(fā)上拱雏,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天,我揣著相機與錄音底扳,去河邊找鬼铸抑。 笑死,一個胖子當著我的面吹牛衷模,可吹牛的內(nèi)容都是我干的鹊汛。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼阱冶,長吁一口氣:“原來是場噩夢啊……” “哼刁憋!你這毒婦竟也來了?” 一聲冷哼從身側響起木蹬,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤至耻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體尘颓,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡走触,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了疤苹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片互广。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖痰催,靈堂內(nèi)的尸體忽然破棺而出盈匾,到底是詐尸還是另有隱情鹤耍,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布损肛,位于F島的核電站凶硅,受9級特大地震影響缝裁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜足绅,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一捷绑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧氢妈,春花似錦粹污、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至加缘,卻和暖如春鸭叙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拣宏。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工沈贝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人勋乾。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓宋下,卻偏偏與公主長得像,于是被迫代替她去往敵國和親辑莫。 傳聞我的和親對象是個殘疾皇子学歧,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容