ThreadLocal原理詳解

2022-04-26 01:51:06 字數 2664 閱讀 4992

首先,它是乙個資料結構,有點像hashmap,可以儲存"key : value"鍵值對,但是乙個threadlocal只能儲存乙個,並且各個執行緒的資料互不干擾。

1 threadlocallocalname = new

threadlocal();

2 localname.set("尋夢");

3 string name = localname.get();

這是為什麼,如何實現?不過之前也說了,threadlocal保證了各個執行緒的資料互不干擾。

看看set(t value)get()方法的原始碼

public

void

set(t value)

public

t get()

}return

setinitialvalue();}

threadlocalmap getmap(thread t)

可以發現,每個執行緒中都有乙個threadlocalmap資料結構,當執行set方法時,其值是儲存在當前執行緒的threadlocals變數中,當執行set方法中,是從當前執行緒的threadlocals變數獲取。

那每個執行緒中的threadloalmap究竟是什麼?

本文分析的是1.7的原始碼。

從名字上看,可以猜到它也是乙個類似hashmap的資料結構,但是在threadlocal中,並沒實現map介面。

在threadloalmap中,也是初始化乙個大小16的entry陣列,entry物件用來儲存每乙個key-value鍵值對,只不過這裡的key永遠都是threadlocal物件,是不是很神奇,通過threadlocal物件的set方法,結果把threadlocal物件自己當做key,放進了threadloalmap中。

這裡需要注意的是,threadloalmap的entry是繼承weakreference,和hashmap很大的區別是,entry中沒有next欄位,所以就不存在鍊錶的情況了。

沒有鍊錶結構,那發生hash衝突了怎麼辦?

先看看threadloalmap中插入乙個key-value的實現

private

void set(threadlocal>key, object value)

if (k == null

) }

tab[i] = new

entry(key, value);

int sz = ++size;

if (!cleansomeslots(i, sz) && sz >=threshold)

rehash();

}

每個threadlocal物件都有乙個hash值threadlocalhashcode,每初始化乙個threadlocal物件,hash值就增加乙個固定的大小0x61c88647

在插入過程中,根據threadlocal物件的hash值,定位到table中的位置i,過程如下:1、如果當前位置是空的,那麼正好,就初始化乙個entry物件放在位置i上;2、不巧,位置i已經有entry物件了,如果這個entry物件的key正好是即將設定的key,那麼重新設定entry中的value;3、很不巧,位置i的entry物件,和即將設定的key沒關係,那麼只能找下乙個空位置;

這樣的話,在get的時候,也會根據threadlocal物件的hash值,定位到table中的位置,然後判斷該位置entry物件中的key是否和get的key一致,如果不一致,就判斷下乙個位置

可以發現,set和get如果衝突嚴重的話,效率很低,因為threadloalmap是thread的乙個屬性,所以即使在自己的**中控制了設定的元素個數,但還是不能控制其它**的行為。

threadlocal可能導致記憶體洩漏,為什麼?先看看entry的實現:

static

class entry extends weakreference>

}

通過之前的分析已經知道,當使用threadlocal儲存乙個value時,會在threadlocalmap中的陣列插入乙個entry物件,按理說key-value都應該以強引用儲存在entry物件中,但在threadlocalmap的實現中,key被儲存到了weakreference物件中。

這就導致了乙個問題,threadlocal在沒有外部強引用時,發生gc時會被**,如果建立threadlocal的執行緒一直持續執行,那麼這個entry物件中的value就有可能一直得不到**,發生記憶體洩露。

如何避免記憶體洩露

既然已經發現有記憶體洩露的隱患,自然有應對的策略,在呼叫threadlocal的get()、set()可能會清除threadlocalmap中key為null的entry物件,這樣對應的value就沒有gc roots可達了,下次gc的時候就可以被**,當然如果呼叫remove方法,肯定會刪除對應的entry物件。

如果使用threadlocal的set方法之後,沒有顯示的呼叫remove方法,就有可能發生記憶體洩露,所以養成良好的程式設計習慣十分重要,使用完threadlocal之後,記得呼叫remove方法。

threadlocallocalname = new

threadlocal();

try finally

ThreadLocal原理詳解

threadlocal稱為執行緒本地變數,其為變數在每個執行緒中都建立了乙個副本,每個執行緒都訪問和修改本執行緒中變數的副本。應用示例 可以看出,為每個執行緒分配乙個變數副本的工作並不是由threadlocal實現的,需要在應用層面實現,threadlocal只是提供乙個容器。如果在應用上為每個執行...

ThreadLocal用法詳解和原理

一 用法 threadlocal用於儲存某個執行緒共享變數 對於同乙個static threadlocal,不同執行緒只能從中get,set,remove自己的變數,而不會影響其他執行緒的變數。1 threadlocal.get 獲取threadlocal中當前執行緒共享變數的值。2 threadl...

ThreadLocal原理探尋

筆者今天趁著專案空隙,學習了下threadlocal這個類,探尋了下多執行緒下如何實現執行緒安全的原理。分享下 package com.suning.sample.transcation public class threadtest public static void main string a...