Java?InheritableThreadLocal使用示例詳解
引子
public class InheritableThreadLocalDemo { private static ThreadLocal<String> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { threadLocal.set("mainThread"); System.out.println("value:"+threadLocal.get()); Thread thread = new Thread(new Runnable() { @Override public void run() { String value = threadLocal.get(); System.out.println("value:"+value); } }); thread.start(); } }
上面代碼中在主線程中設(shè)置了一個(gè)ThreadLocal變量,并將其值設(shè)置為mainThread
。然后有在主線程中開啟了一個(gè)子線程thread
,并試圖獲取在主線程中set的ThreadLocal變量的值。但是結(jié)果如下:
value:mainThread
value:null
通過前面的文章介紹,對(duì)于上面的結(jié)果我們也就非常容易理解了。每個(gè)線程都會(huì)有一個(gè)自己的ThreadLocalMap,所以子線程在調(diào)用get方法拿值的時(shí)候其實(shí)訪問的是自己的ThreadLocalMap,這個(gè)Map和主線程的Map是兩個(gè)不同的對(duì)象,所以肯定是拿不到值的。
那么Java中有沒有類似的對(duì)象能實(shí)現(xiàn)上面的功能呢?有,InheritableThreadLocal
就能實(shí)現(xiàn)這樣的功能,這個(gè)類能讓子線程繼承父線程中已經(jīng)設(shè)置的ThreadLocal值。
InheritableThreadLocal簡(jiǎn)單使用
還是以上面的列子為列,我們只需要將ThreadLocal變成InheritableThreadLocal就行了。
public class InheritableThreadLocalDemo { private static InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>(); public static void main(String[] args) { threadLocal.set("mainThread"); System.out.println("value:"+threadLocal.get()); Thread thread = new Thread(new Runnable() { @Override public void run() { String value = threadLocal.get(); System.out.println("value:"+value); } }); thread.start(); } }
執(zhí)行結(jié)果如下:
value:mainThread
value:mainThread
InheritableThreadLocal原理分析
先看下InheritableThreadLocal的源代碼:
public class InheritableThreadLocal<T> extends ThreadLocal<T> { protected T childValue(T parentValue) { return parentValue; } ThreadLocalMap getMap(Thread t) { return t.inheritableThreadLocals; } void createMap(Thread t, T firstValue) { t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue); } }
這個(gè)類繼承了ThreadLocal,并且重寫了getMap和createMap方法,區(qū)別就是將 ThreadLocal 中的 threadLocals 換成了 inheritableThreadLocals,這兩個(gè)變量都是ThreadLocalMap類型,并且都是Thread類的屬性。
下面就一步步來看下InheritableThreadLocal為什么能拿到父線程中的ThreadLocal值。
step1:InheritableThreadLocal獲取值先調(diào)用了get方法,所以我們直接看看get方法都做了些啥。
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(); }
從上面的代碼可以看出,get方法和ThreadLocal中是一樣的,唯一有區(qū)別的就是其中的getMap方法重寫了,返回的是inheritableThreadLocals屬性。這個(gè)屬性也是一個(gè)ThreadLocalMap類型的變量。那么從這邊就可以推斷出來:肯定是在某處將父線程中的ThreadLocal值賦值到了子線程的inheritableThreadLocals中。
step2:在源代碼中搜索哪些地方使用到了inheritableThreadLocals
這個(gè)屬性,最后找到這段代碼:
private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc) { if (name == null) { throw new NullPointerException("name cannot be null"); } this.name = name.toCharArray(); Thread parent = currentThread(); SecurityManager security = System.getSecurityManager(); if (g == null) { if (security != null) { g = security.getThreadGroup(); } if (g == null) { g = parent.getThreadGroup(); } } g.checkAccess(); if (security != null) { if (isCCLOverridden(getClass())) { security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); } } g.addUnstarted(); this.group = g; this.daemon = parent.isDaemon(); this.priority = parent.getPriority(); if (security == null || isCCLOverridden(parent.getClass())) this.contextClassLoader = parent.getContextClassLoader(); else this.contextClassLoader = parent.contextClassLoader; this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext(); this.target = target; setPriority(priority); //1. 這邊先判斷了父線程中inheritableThreadLocals屬性是否為空,不為空的話就復(fù)制給子線程 if (parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); /* Stash the specified stack size in case the VM cares */ this.stackSize = stackSize; /* Set thread ID */ tid = nextThreadID(); }
上面的代碼印證了我們的猜想。需要注意的是一旦子線程被創(chuàng)建以后,再操作父線程中的ThreadLocal變量,那么子線程是不能感知的。因?yàn)楦妇€程和子線程還是擁有各自的ThreadLocalMap,只是在創(chuàng)建子線程的“一剎那”將父線程的ThreadLocalMap復(fù)制給子線程,后續(xù)兩者就沒啥關(guān)系了。
到此這篇關(guān)于Java InheritableThreadLocal使用示例詳解的文章就介紹到這了,更多相關(guān)Java InheritableThreadLocal內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot整合screw實(shí)現(xiàn)數(shù)據(jù)庫(kù)文檔自動(dòng)生成的示例代碼
這篇文章主要介紹了SpringBoot整合screw實(shí)現(xiàn)數(shù)據(jù)庫(kù)文檔自動(dòng)生成的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09Spring3.1.1+MyBatis3.1.1的增、刪、查、改以及分頁(yè)和事務(wù)管理
這篇文章主要介紹了Spring3.1.1+MyBatis3.1.1的增、刪、查、改以及分頁(yè)和事務(wù)管理的相關(guān)資料,需要的朋友可以參考下2016-01-01SpringBoot參數(shù)校驗(yàn)的方法總結(jié)
今天帶大家學(xué)習(xí)SpringBoot參數(shù)校驗(yàn)的方法,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有很好地幫助,需要的朋友可以參考下2021-05-05淺談一個(gè)基礎(chǔ)的SpringBoot項(xiàng)目該包含哪些
這篇文章主要介紹了淺談一個(gè)基礎(chǔ)的SpringBoot項(xiàng)目該包含哪些,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10java中生成任意之間數(shù)的隨機(jī)數(shù)詳解
這篇文章主要介紹了java中生成任意之間數(shù)的隨機(jī)數(shù)詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09Java全面細(xì)致講解Cookie與Session及kaptcha驗(yàn)證碼的使用
web開發(fā)階段我們主要是瀏覽器和服務(wù)器之間來進(jìn)行交互。瀏覽器和服務(wù)器之間的交互就像人和人之間進(jìn)行交流一樣,但是對(duì)于機(jī)器來說,在一次請(qǐng)求之間只是會(huì)攜帶著本次請(qǐng)求的數(shù)據(jù)的,但是可能多次請(qǐng)求之間是會(huì)有聯(lián)系的,所以提供了會(huì)話機(jī)制2022-06-06