ThreadLocal 的開發(fā)應(yīng)用
作者:KerryWu
來(lái)源:SegmentFault 思否社區(qū)
ThreadLocal是線程私有的局部變量存儲(chǔ)容器,可以理解成每個(gè)線程都有自己專屬的存儲(chǔ)容器,用來(lái)存儲(chǔ)線程私有變量。ThreadLocal 在日常開發(fā)框架中應(yīng)用廣泛,但用不好也會(huì)出現(xiàn)各種問題,本文就此講解一下。
1. 應(yīng)用場(chǎng)景
ThreadLocal 的常見應(yīng)用場(chǎng)景有兩種:
多線程并發(fā)場(chǎng)景中,用來(lái)保障線程安全。 處理較為復(fù)雜的業(yè)務(wù)時(shí),使用ThreadLocal代替參數(shù)的顯示傳遞。
1.1. 保障線程安全
1.2. 顯示傳遞參數(shù)
public class UserProfileThread {
private static ThreadLocal<UserProfile> USER_PROFILE_TL =new ThreadLocal<>();
public static void setUserProfile(UserProfile userProfile){
USER_PROFILE_TL.set(userProfile);
}
public static UserProfile getUserProfile() {
return USER_PROFILE_TL.get();
}
public static String getCurrentUser() {
return Optional.ofNullable(USER_PROFILE_TL.get())
.map(UserProfile::getUid)
.orElse(UserProfile.ANONYMOUS_USER);
}
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
UserProfile userProfile = null;
// ... 驗(yàn)證和獲取用戶信息 userProfile
UserProfileThread.setUserProfile(userProfile);
filterChain.doFilter(servletRequest, servletResponse);
}
//獲取當(dāng)前用戶
String uid=UserProfileThread.getCurrentUser();
//獲取當(dāng)前用戶對(duì)象
UserProfile user=UserProfileThread.getUserProfile();
2. 實(shí)現(xiàn)原理
public class ThreadLocal<T> {
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
// 省略其他方法
}
public class Thread implements Runnable {
ThreadLocal.ThreadLocalMap threadLocals = null;
//省略其他
}
每個(gè)線程是一個(gè)Thread實(shí)例,其內(nèi)部維護(hù)一個(gè)threadLocals的實(shí)例成員,其類型是ThreadLocal.ThreadLocalMap。 ThreadLocal本身并不是一個(gè)容器,我們存取的value實(shí)際上存儲(chǔ)在ThreadLocalMap中,ThreadLocal只是作為TheadLocalMap的key。
3. 注意事項(xiàng)
線程復(fù)用:在“獲取接口的當(dāng)前請(qǐng)求用戶”的例子中,Tomcat中是通過(guò)線程池來(lái)處理用戶請(qǐng)求的,而線程池中線程是復(fù)用的??隙〞?huì)出現(xiàn)一個(gè)線程前后被不同用戶的接口請(qǐng)求復(fù)用的情況,因此需要對(duì)用過(guò)的ThreaLocal變量進(jìn)行覆蓋或清除。 內(nèi)存溢出:由于ThreadLocalMap的生命周期跟Thread一樣長(zhǎng),如果創(chuàng)建的ThreadLocal變量很多,即對(duì)應(yīng)的key占用的內(nèi)存很大,但卻沒有手動(dòng)刪除,到了一定程度就會(huì)導(dǎo)致內(nèi)存泄漏。

評(píng)論
圖片
表情
