ThreadLocal原理分析與使用場景
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
作者 | 阿凡盧
來源 | urlify.cn/myyUVb
76套java從入門到精通實(shí)戰(zhàn)課程分享
什么是ThreadLocal變量
ThreadLoal 變量,線程局部變量,同一個(gè) ThreadLocal 所包含的對象,在不同的 Thread 中有不同的副本。這里有幾點(diǎn)需要注意:
因?yàn)槊總€(gè) Thread 內(nèi)有自己的實(shí)例副本,且該副本只能由當(dāng)前 Thread 使用。這是也是 ThreadLocal 命名的由來。
既然每個(gè) Thread 有自己的實(shí)例副本,且其它 Thread 不可訪問,那就不存在多線程間共享的問題。
ThreadLocal 提供了線程本地的實(shí)例。它與普通變量的區(qū)別在于,每個(gè)使用該變量的線程都會初始化一個(gè)完全獨(dú)立的實(shí)例副本。ThreadLocal 變量通常被private static修飾。當(dāng)一個(gè)線程結(jié)束時(shí),它所使用的所有 ThreadLocal 相對的實(shí)例副本都可被回收。
總的來說,ThreadLocal 適用于每個(gè)線程需要自己獨(dú)立的實(shí)例且該實(shí)例需要在多個(gè)方法中被使用,也即變量在線程間隔離而在方法或類間共享的場景。
ThreadLocal實(shí)現(xiàn)原理
首先 ThreadLocal 是一個(gè)泛型類,保證可以接受任何類型的對象。
因?yàn)橐粋€(gè)線程內(nèi)可以存在多個(gè) ThreadLocal 對象,所以其實(shí)是 ThreadLocal 內(nèi)部維護(hù)了一個(gè) Map ,這個(gè) Map 不是直接使用的 HashMap ,而是 ThreadLocal 實(shí)現(xiàn)的一個(gè)叫做 ThreadLocalMap 的靜態(tài)內(nèi)部類。而我們使用的 get()、set() 方法其實(shí)都是調(diào)用了這個(gè)ThreadLocalMap類對應(yīng)的 get()、set() 方法。例如下面的 set 方法:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
get方法:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
return (T)map.get(this);
// Maps are constructed lazily. if the map for this thread
// doesn't exist, create it, with this ThreadLocal and its
// initial value as its only entry.
T value = initialValue();
createMap(t, value);
return value;
}
createMap方法:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
} ThreadLocalMap是個(gè)靜態(tài)的內(nèi)部類:
static class ThreadLocalMap {
........
} 最終的變量是放在了當(dāng)前線程的 ThreadLocalMap 中,并不是存在 ThreadLocal 上,ThreadLocal 可以理解為只是ThreadLocalMap的封裝,傳遞了變量值。
內(nèi)存泄漏問題
實(shí)際上 ThreadLocalMap 中使用的 key 為 ThreadLocal 的弱引用,弱引用的特點(diǎn)是,如果這個(gè)對象只存在弱引用,那么在下一次垃圾回收的時(shí)候必然會被清理掉。
所以如果 ThreadLocal 沒有被外部強(qiáng)引用的情況下,在垃圾回收的時(shí)候會被清理掉的,這樣一來 ThreadLocalMap中使用這個(gè) ThreadLocal 的 key 也會被清理掉。但是,value 是強(qiáng)引用,不會被清理,這樣一來就會出現(xiàn) key 為 null 的 value。
ThreadLocalMap實(shí)現(xiàn)中已經(jīng)考慮了這種情況,在調(diào)用 set()、get()、remove() 方法的時(shí)候,會清理掉 key 為 null 的記錄。如果說會出現(xiàn)內(nèi)存泄漏,那只有在出現(xiàn)了 key 為 null 的記錄后,沒有手動調(diào)用 remove() 方法,并且之后也不再調(diào)用 get()、set()、remove() 方法的情況下。
建議回收自定義的ThreadLocal變量,尤其在線程池場景下,線程經(jīng)常會被復(fù)用,如果不清理自定義的 ThreadLocal變量,可能會影響后續(xù)業(yè)務(wù)邏輯和造成內(nèi)存泄露等問題。盡量在代理中使用try-finally塊進(jìn)行回收:
objectThreadLocal.set(userInfo);
try {
// ...
}
finally {
objectThreadLocal.remove();
}
使用場景
如上文所述,ThreadLocal 適用于如下兩種場景
每個(gè)線程需要有自己單獨(dú)的實(shí)例
實(shí)例需要在多個(gè)方法中共享,但不希望被多線程共享
對于第一點(diǎn),每個(gè)線程擁有自己實(shí)例,實(shí)現(xiàn)它的方式很多。例如可以在線程內(nèi)部構(gòu)建一個(gè)單獨(dú)的實(shí)例。ThreadLoca 可以以非常方便的形式滿足該需求。
對于第二點(diǎn),可以在滿足第一點(diǎn)(每個(gè)線程有自己的實(shí)例)的條件下,通過方法間引用傳遞的形式實(shí)現(xiàn)。ThreadLocal 使得代碼耦合度更低,且實(shí)現(xiàn)更優(yōu)雅。
1)存儲用戶Session
一個(gè)簡單的用ThreadLocal來存儲Session的例子:
private static final ThreadLocal threadSession = new ThreadLocal();
public static Session getSession() throws InfrastructureException {
Session s = (Session) threadSession.get();
try {
if (s == null) {
s = getSessionFactory().openSession();
threadSession.set(s);
}
} catch (HibernateException ex) {
throw new InfrastructureException(ex);
}
return s;
}
2)解決線程安全的問題
比如Java7中的SimpleDateFormat不是線程安全的,可以用ThreadLocal來解決這個(gè)問題:
public class DateUtil {
private static ThreadLocal<SimpleDateFormat> format1 = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
public static String formatDate(Date date) {
return format1.get().format(date);
}
}
這里的DateUtil.formatDate()就是線程安全的了。(Java8里的 java.time.format.DateTimeFormatter是線程安全的,Joda time里的DateTimeFormat也是線程安全的)。
ThreadLocalRandom
ThreadLocalRandom使用ThreadLocal的原理,讓每個(gè)線程內(nèi)持有一個(gè)本地的種子變量,該種子變量只有在使用隨機(jī)數(shù)時(shí)候才會被初始化,多線程下計(jì)算新種子時(shí)候是根據(jù)自己線程內(nèi)維護(hù)的種子變量進(jìn)行更新,從而避免了競爭。
用法:
ThreadLocalRandom.current().nextInt(100)
鋒哥最新SpringCloud分布式電商秒殺課程發(fā)布
??????
??長按上方微信二維碼 2 秒
感謝點(diǎn)贊支持下哈 
