日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

Java并发编程实战~ThreadLocal

發(fā)布時(shí)間:2024/7/23 java 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java并发编程实战~ThreadLocal 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

ThreadLocal 的使用方法

static class SafeDateFormat {// 定義 ThreadLocal 變量static final ThreadLocal<DateFormat>tl = ThreadLocal.withInitial(()-> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));static DateFormat get(){return tl.get();} }// 不同線程執(zhí)行下面代碼 // 返回的 df 是不同的 DateFormat df = SafeDateFormat.get();

ThreadLocal 的工作原理

錯(cuò)誤的實(shí)現(xiàn)方案

?

class MyThreadLocal<T> {Map<Thread, T> locals = new ConcurrentHashMap<>();// 獲取線程變量 T get() {return locals.get(Thread.currentThread());}// 設(shè)置線程變量void set(T t) {locals.put(Thread.currentThread(), t);} }

JDK實(shí)現(xiàn)方案

????????Thread 這個(gè)類(lèi)內(nèi)部有一個(gè)私有屬性 threadLocals,其類(lèi)型就是 ThreadLocalMap,ThreadLocalMap 的 Key 是ThreadLocal。

?

class Thread {// 內(nèi)部持有 ThreadLocalMapThreadLocal.ThreadLocalMap threadLocals; }class ThreadLocal<T>{public T get() {// 首先獲取線程持有的//ThreadLocalMapThreadLocalMap map = Thread.currentThread().threadLocals;// 在 ThreadLocalMap 中// 查找變量Entry e = map.getEntry(this);return e.value; }static class ThreadLocalMap{// 內(nèi)部是數(shù)組而不是 MapEntry[] table;// 根據(jù) ThreadLocal 查找 EntryEntry getEntry(ThreadLocal key){// 省略查找邏輯}//Entry 定義static class Entry extends WeakReference<ThreadLocal>{Object value;}} }

????????ThreadLocal 僅僅是一個(gè)代理工具類(lèi),內(nèi)部并不持有任何與線程相關(guān)的數(shù)據(jù),所有和線程相關(guān)的數(shù)據(jù)都存儲(chǔ)在 Thread 里面,這樣的設(shè)計(jì)容易理解。而從數(shù)據(jù)的親緣性上來(lái)講,ThreadLocalMap 屬于 Thread 也更加合理。

????????當(dāng)然還有一個(gè)更加深層次的原因,那就是不容易產(chǎn)生內(nèi)存泄露。在我們的設(shè)計(jì)方案中,ThreadLocal 持有的 Map 會(huì)持有 Thread 對(duì)象的引用,這就意味著,只要 ThreadLocal對(duì)象存在,那么 Map 中的 Thread 對(duì)象就永遠(yuǎn)不會(huì)被回收。ThreadLocal 的生命周期往往都比線程要長(zhǎng),所以這種設(shè)計(jì)方案很容易導(dǎo)致內(nèi)存泄露。而 Java 的實(shí)現(xiàn)中 Thread 持有ThreadLocalMap,而且 ThreadLocalMap 里對(duì) ThreadLocal 的引用還是弱引用(WeakReference),所以只要 Thread 象可以被回收,那么 ThreadLocalMap 就能被回收。Java 的這種實(shí)現(xiàn)方案雖然看上去復(fù)雜一些,但是更加安全。

ThreadLocal 與內(nèi)存泄露

????????在線程池中使用 ThreadLocal 為什么可能導(dǎo)致內(nèi)存泄露呢?原因就出在線程池中線程的存活時(shí)間太長(zhǎng),往往都是和程序同生共死的,這就意味著 Thread 持有的 ThreadLocalMap一直都不會(huì)被回收,再加上 ThreadLocalMap 中的 Entry 對(duì) ThreadLocal 是弱引用(WeakReference),所以只要 ThreadLocal 結(jié)束了自己的生命周期是可以被回收掉的。但是 Entry 中的 Value 卻是被 Entry 強(qiáng)引用的,所以即便 Value 的生命周期結(jié)束了,Value 也是無(wú)法被回收的,從而導(dǎo)致內(nèi)存泄露。

????????那在線程池中,我們?cè)撊绾握_使用 ThreadLocal 呢?其實(shí)很簡(jiǎn)單,既然 JVM 不能做到自動(dòng)釋放對(duì) Value 的強(qiáng)引用,那我們手動(dòng)釋放就可以了。

ExecutorService es; ThreadLocal tl; es.execute(()->{//ThreadLocal 增加變量tl.set(obj);try {// 省略業(yè)務(wù)邏輯代碼}finally {// 手動(dòng)清理 ThreadLocal tl.remove();} });

InheritableThreadLocal 與繼承性

????????通過(guò) ThreadLocal 創(chuàng)建的線程變量,其子線程是無(wú)法繼承的。也就是說(shuō)你在線程中通過(guò)
ThreadLocal 創(chuàng)建了線程變量 V,而后該線程創(chuàng)建了子線程,你在子線程中是無(wú)法通過(guò)
ThreadLocal 來(lái)訪問(wèn)父線程的線程變量 V 的。

????????如果你需要子線程繼承父線程的線程變量,那該怎么辦呢?其實(shí)很簡(jiǎn)單,Java 提供了
InheritableThreadLocal 來(lái)支持這種特性。

????????不過(guò),不建議你在線程池中使用 InheritableThreadLocal,不僅僅是因?yàn)樗哂蠺hreadLocal 相同的缺點(diǎn)——可能導(dǎo)致內(nèi)存泄露,更重要的原因是:線程池中線程的創(chuàng)建是動(dòng)態(tài)的,很容易導(dǎo)致繼承關(guān)系錯(cuò)亂,如果你的業(yè)務(wù)邏輯依賴(lài) InheritableThreadLocal,那么很可能導(dǎo)致業(yè)務(wù)邏輯計(jì)算錯(cuò)誤,而這個(gè)錯(cuò)誤往往比內(nèi)存泄露更要命。

總結(jié)

以上是生活随笔為你收集整理的Java并发编程实战~ThreadLocal的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。