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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

ThreadLocal的原理和FastThreadLocal的优势

發(fā)布時(shí)間:2025/3/19 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ThreadLocal的原理和FastThreadLocal的优势 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、ThreadLocal的編寫測(cè)試

1.只需定義一個(gè)靜態(tài)全局的ThreadLocal變量,然后在線程的執(zhí)行方法里面,對(duì)這個(gè)對(duì)象的某個(gè)方法,set后,同一個(gè)線程get,能正常取出數(shù)據(jù)。

2.線程池使用ThreadLocal變量,要注意,任務(wù)執(zhí)行完要?jiǎng)h除ThreadLocal數(shù)據(jù),防止臟數(shù)據(jù)傳播到同一個(gè)線程的下一個(gè)任務(wù)中。

@Slf4j public class ThreadLocalTest {private static ThreadLocal<String> nameThreadLocal = new ThreadLocal<String>(); // private static ThreadLocal<Integer> ageThreadLocal = new ThreadLocal<Integer>();private static ExecutorService executor = new ThreadPoolExecutor(3,6,10,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(100),new NamedThreadFactory("local",false));private ThreadLocalTest() {}public void produce(String name,int age) {try{log.debug(" old name:{},threadname:{}",nameThreadLocal.get(),Thread.currentThread().getName());nameThreadLocal.set(name);log.debug(" new name:{},threadname:{}",nameThreadLocal.get(),Thread.currentThread().getName());}finally {nameThreadLocal.remove();}}public static void main(String[] args) {ThreadLocalTest threadLocalTest = new ThreadLocalTest();for ( int i = 0; i < 1; i++) {int k = i;executor.execute(()->{threadLocalTest.produce(String.format("lilei:%d",k) , k);});}try {executor.awaitTermination(0,TimeUnit.SECONDS);executor.shutdown();} catch (InterruptedException e) {e.printStackTrace();}} }

二、ThreadLocal的原理分析

1.在Thread類內(nèi)部有一個(gè)threadLocals的map對(duì)象,用來保存每個(gè)線程下的LOCAL數(shù)據(jù)

public class Thread implements Runnable {/* ThreadLocal values pertaining to this thread. This map is maintained* by the ThreadLocal class. */ThreadLocal.ThreadLocalMap threadLocals = null;/** InheritableThreadLocal values pertaining to this thread. This map is* maintained by the InheritableThreadLocal class.*/ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

2.這個(gè)map對(duì)象是一個(gè)使用數(shù)組來存儲(chǔ)的哈希表(ThreadLocal<?>作為KEY,值為具體的值),因?yàn)槌绦蛑锌赡茉谕粋€(gè)線程定義有多個(gè)ThreadLocal變量,所以為一個(gè)map。

static class ThreadLocalMap {static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}private Entry[] table;ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {table = new Entry[INITIAL_CAPACITY];int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);table[i] = new Entry(firstKey, firstValue);size = 1;setThreshold(INITIAL_CAPACITY);}

3.ThreadLocal.set原理,根據(jù)當(dāng)前線程對(duì)象去getMap就是取出上述第一步線程對(duì)象的threadLocals的map對(duì)象,如果map存在,就是從MAP中根據(jù)當(dāng)前ThreadLocal對(duì)象的KEY來設(shè)置相應(yīng)的數(shù)據(jù)。如果不存在map,則創(chuàng)建一個(gè)新的map,并賦值為Thread的成員變量threadLocals,然后在這個(gè)新的map中對(duì)當(dāng)前<ThreadLocalKey,value>進(jìn)行插入。

public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);}ThreadLocalMap getMap(Thread t) {return t.threadLocals;}

4.ThreadLocal.get原理,同set一樣,只不過一個(gè)獲取,一個(gè)設(shè)置,但注意的是,如果MAP不存在,則會(huì)調(diào)用初始化方法,進(jìn)行初始值。

public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();}private T setInitialValue() {T value = initialValue();Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);return value;}

5.Threadlocal.remove就是對(duì)map中當(dāng)前ThreadLocal的KEY進(jìn)行刪除。

public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());if (m != null)m.remove(this);}

三、FastThreadLocal的原理

1.測(cè)試代碼,使用NioEventLoopGroup默認(rèn)創(chuàng)建的線程池中的線程就是FastThreadLocalThread,這個(gè)類繼承于Thread,FastThreadLocal就是替換ThreadLocal類的,當(dāng)然FastThreadLocal也能使用于普通線程,只需要提交 線程的Runnable包裝一下FastThreadLocalRunnable,

@Slf4j public class ThreadLocalTest {private static FastThreadLocal<String> nameThreadLocal = new FastThreadLocal<String>(){@Overrideprotected String initialValue() {return "init value";}};private static FastThreadLocal<String> nameThreadLocal2 = new FastThreadLocal<String>(){@Overrideprotected String initialValue() {return "init value";}};private static FastThreadLocal<String> nameThreadLocal3 = new FastThreadLocal<String>(){@Overrideprotected String initialValue() {return "init value";}};// private static ThreadLocal<Integer> ageThreadLocal = new ThreadLocal<Integer>();private static ExecutorService executor = new ThreadPoolExecutor(3,6,10,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(100),new NamedThreadFactory("local",false));private ThreadLocalTest() {}public void produce(String name,int age) {try{ // log.debug(" old name:{},threadname:{}",nameThreadLocal.get() // ,Thread.currentThread().getName());nameThreadLocal.set("first-" + name);nameThreadLocal2.set( "second-" + name );nameThreadLocal3.set( "third-" + name );int index1 = (int) ReflectUtil.getFieldValue(nameThreadLocal,"index");int index2 = (int) ReflectUtil.getFieldValue(nameThreadLocal2,"index");int index3 = (int) ReflectUtil.getFieldValue(nameThreadLocal3,"index");log.debug(" new name index:{}:{},second index:{}:{},third index:{}:{},threadname:{}",index1,nameThreadLocal.get(),index2,nameThreadLocal2.get(),index3,nameThreadLocal3.get(),Thread.currentThread().getName());Thread curThread =Thread.currentThread();if (curThread instanceof FastThreadLocalThread){FastThreadLocalThread fastThreadLocalThread = (FastThreadLocalThread) curThread;Object[] indexedVariables = (Object[]) ReflectUtil.getFieldValue(fastThreadLocalThread.threadLocalMap(),"indexedVariables");log.debug(" threadLocalMap threadname:{}",Thread.currentThread().getName());}}finally { // nameThreadLocal.remove();}}public void remove() {try{FastThreadLocal.removeAll();log.debug(" new name ,threadname:{}",Thread.currentThread().getName());}finally { // nameThreadLocal.remove();}}public void testNettyThreadLocal( ){NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(3);for ( int i = 0; i < 2; i++) {int k = i;try {nioEventLoopGroup.next().submit(()->{this.produce(String.format("lilei-%d",k) , k);}).await();} catch (InterruptedException e) {e.printStackTrace();}}for ( int i = 0; i < 2; i++) {int k = i;try {nioEventLoopGroup.next().submit(()->{this.remove();}).await();} catch (InterruptedException e) {e.printStackTrace();}}try {nioEventLoopGroup.awaitTermination(60 * 60,TimeUnit.SECONDS);nioEventLoopGroup.shutdownGracefully();} catch (InterruptedException e) {e.printStackTrace();}}public static void main(String[] args) {ThreadLocalTest threadLocalTest = new ThreadLocalTest();threadLocalTest.testNettyThreadLocal();} }

2.回憶下JDK的ThreadLocal,只是每個(gè)Thread下面有個(gè)ThreadLocalMap的成員變量,以<ThreadLocal,val>作為格式進(jìn)行存儲(chǔ),然后設(shè)置和獲取時(shí)都是獲取當(dāng)前線程類下的ThreadLocalMap哈希數(shù)組進(jìn)行操作。那么FastThreadLocal也是一樣,把Thread類換成

FastThreadLocalThread,在這個(gè)線程類下面有一個(gè)private InternalThreadLocalMap threadLocalMap;也是用來保存當(dāng)前線程下私有的配置數(shù)據(jù)。 public class FastThreadLocalThread extends Thread {private InternalThreadLocalMap threadLocalMap;

3.我們重點(diǎn)來看下threadLocalMap的數(shù)據(jù)結(jié)構(gòu),這是以一個(gè)對(duì)象數(shù)據(jù)存儲(chǔ)的,有一個(gè)成員變量

Object[] indexedVariables;還有一個(gè)靜態(tài)全局的索引自增器nextIndex,每個(gè)FastThreadLocal類中有一個(gè)index的整形變量(在構(gòu)造函數(shù)中通過nextIndex原子自增進(jìn)行唯一),這就是存儲(chǔ)在每個(gè)FastThreadLocalThread的私有threadLocalMap中的indexedVariables的索引下標(biāo)。 class UnpaddedInternalThreadLocalMap {static final ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = new ThreadLocal<InternalThreadLocalMap>();static final AtomicInteger nextIndex = new AtomicInteger();public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap {class FastThreadLocal{public FastThreadLocal() {index = InternalThreadLocalMap.nextVariableIndex();}

4.我們來看FastThreadLocal.set方法。

FastThreadLocal public final void set(V value) {if (value != InternalThreadLocalMap.UNSET) {InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();setKnownNotUnset(threadLocalMap, value);} else {remove();}}

5.這里面調(diào)用了InternalThreadLocalMap.get

public static InternalThreadLocalMap get() {Thread thread = Thread.currentThread();if (thread instanceof FastThreadLocalThread) {return fastGet((FastThreadLocalThread) thread);} else {return slowGet();}}

6.判斷當(dāng)前如果是FastThreadLocalThread,則直接獲取FastThreadLocalThread的threadLocalMap成員變量,不存在就新建一個(gè),進(jìn)行關(guān)聯(lián)。注意在第一次新建時(shí),會(huì)重新新建一個(gè)object[]的indexedVariables對(duì)象,所以是每個(gè)線程一份私有。

private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();if (threadLocalMap == null) {thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());}return threadLocalMap;}private InternalThreadLocalMap() {super(newIndexedVariableTable());}private static Object[] newIndexedVariableTable() {Object[] array = new Object[INDEXED_VARIABLE_TABLE_INITIAL_SIZE];Arrays.fill(array, UNSET);return array;}

7.如果判斷是普通線程,則通過借助JDK的ThreadLocal存儲(chǔ)一個(gè)slowThreadLocalMap,其它流程一樣。

private static InternalThreadLocalMap slowGet() {ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap;InternalThreadLocalMap ret = slowThreadLocalMap.get();if (ret == null) {ret = new InternalThreadLocalMap();slowThreadLocalMap.set(ret);}return ret;}class UnpaddedInternalThreadLocalMap {static final ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = new ThreadLocal<InternalThreadLocalMap>();

8.我們繼續(xù)回到第4步,獲取到MAP后,就開始在這個(gè)map中設(shè)置值了。setKnownNotUnset

private void setKnownNotUnset(InternalThreadLocalMap threadLocalMap, V value) {if (threadLocalMap.setIndexedVariable(index, value)) {addToVariablesToRemove(threadLocalMap, this);}}

9.這里面就是調(diào)用InternalThreadLocalMap的setIndexedVariable,這就是在FastThreadLocalThread的threadLocalMap成員變量中的indexdVariables數(shù)組利用FastThreadLocal當(dāng)前設(shè)置的index下標(biāo)普通的賦值,注意,這里面,有返回是不是該線程第一次使用此ThreadLocal,如果是就返回TRUE,否則FALSE,并且作了一個(gè)動(dòng)態(tài)擴(kuò)容。

public boolean setIndexedVariable(int index, Object value) {Object[] lookup = indexedVariables;if (index < lookup.length) {Object oldValue = lookup[index];lookup[index] = value;return oldValue == UNSET;} else {expandIndexedVariableTableAndSet(index, value);return true;}}

10.如果返回true,證明是第一次對(duì)下標(biāo)賦值,則將此ThreadLocal作為KEY存到一個(gè)hasSet中,并將這個(gè)set保存到indexVaribables數(shù)組的第0號(hào)位置。這個(gè)第0號(hào)位置的set其實(shí)就是當(dāng)前線程中所有擁有的ThreadLocal對(duì)象,以便在這個(gè)線程要銷毀時(shí)(特別是線程池任務(wù)執(zhí)行完畢,要清除線程數(shù)據(jù),防止下次線程重用出現(xiàn)臟數(shù)據(jù)),清空當(dāng)前線程的所有ThreadLocal對(duì)象所用。即調(diào)用FastThreadLoca.removeAll()靜態(tài)方法。

private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);Set<FastThreadLocal<?>> variablesToRemove;if (v == InternalThreadLocalMap.UNSET || v == null) {variablesToRemove = Collections.newSetFromMap(new IdentityHashMap<FastThreadLocal<?>, Boolean>());threadLocalMap.setIndexedVariable(variablesToRemoveIndex, variablesToRemove);} else {variablesToRemove = (Set<FastThreadLocal<?>>) v;}variablesToRemove.add(variable);}

11.通過日志,我們看下,由于我們建了三個(gè)FastThreadlocal,并且啟動(dòng)了2個(gè)線程進(jìn)行相應(yīng)的set方法,發(fā)現(xiàn)每個(gè)FastThreadLocal的index在不同的Thread是相同的,即index是全局自增唯一,只是這個(gè)index所對(duì)應(yīng)的內(nèi)存數(shù)據(jù)為每個(gè)線程私有的map。

10:08:29.147 [nioEventLoopGroup-2-1] DEBUG com.tpw.summaryday.test.ThreadLocalTest - new name index:1:first-lilei-0,second index:2:second-lilei-0,third index:3:third-lilei-0,threadname:nioEventLoopGroup-2-1 10:08:29.174 [nioEventLoopGroup-2-2] DEBUG com.tpw.summaryday.test.ThreadLocalTest - new name index:1:first-lilei-1,second index:2:second-lilei-1,third index:3:third-lilei-1,threadname:nioEventLoopGroup-2-2

12.get,remove原理一樣,不再重復(fù)。

13.我們來查看下FastThreadLocalThread的threadLocalMap成員變量中的indexdVariables數(shù)組內(nèi)存的數(shù)據(jù)結(jié)構(gòu)。

?

?可以看到第0個(gè)元素為一個(gè)set,存儲(chǔ)的全是當(dāng)前線程的Threadlocal的KEY。后面的元素則是具體的KEY對(duì)應(yīng)的值。

四、FastThreadLocal與ThreadLocal的區(qū)別和優(yōu)勢(shì)

1.ThreadLocal使用傳統(tǒng)的對(duì)KEY取哈希算法計(jì)算數(shù)組的下標(biāo),并且發(fā)現(xiàn)沖突時(shí)依次向后推一個(gè)空位,效率會(huì)低些。

2.FastThreadLocal中的map則在初始化時(shí)確定一個(gè)所有FastThreadLocal變量全局唯一的index(通過一個(gè)全局自增nextIndex),省去了計(jì)算hash和解決沖突的時(shí)間。

總結(jié)

以上是生活随笔為你收集整理的ThreadLocal的原理和FastThreadLocal的优势的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

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