本博客系列是學習併發編程過程當中的記錄總結。因爲文章比較多,寫的時間也比較散,因此我整理了個目錄貼(傳送門),方便查閱。html
併發編程系列博客傳送門java
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(); } }
上面代碼中在主線程中設置了一個ThreadLocal變量,並將其值設置爲mainThread
。而後有在主線程中開啓了一個子線程thread
,並試圖獲取在主線程中set的ThreadLocal變量的值。可是結果以下:編程
value:mainThread value:null
經過前面的文章介紹,對於上面的結果咱們也就很是容易理解了。每一個線程都會有一個本身的ThreadLocalMap,因此子線程在調用get方法拿值的時候其實訪問的是本身的ThreadLocalMap,這個Map和主線程的Map是兩個不一樣的對象,因此確定是拿不到值的。併發
那麼Java中有沒有相似的對象能實現上面的功能呢?有,InheritableThreadLocal
就能實現這樣的功能,這個類能讓子線程繼承父線程中已經設置的ThreadLocal值。ide
仍是以上面的列子爲列,咱們只須要將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(); } }
執行結果以下:this
value:mainThread value:mainThread
先看下InheritableThreadLocal的源代碼:spa
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); } }
這個類繼承了ThreadLocal,而且重寫了getMap和createMap方法,區別就是將 ThreadLocal 中的 threadLocals 換成了 inheritableThreadLocals,這兩個變量都是ThreadLocalMap類型,而且都是Thread類的屬性。線程
下面就一步步來看下InheritableThreadLocal爲何能拿到父線程中的ThreadLocal值。code
step1:InheritableThreadLocal獲取值先調用了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中是同樣的,惟一有區別的就是其中的getMap方法重寫了,返回的是inheritableThreadLocals屬性。這個屬性也是一個ThreadLocalMap類型的變量。那麼從這邊就能夠推斷出來:確定是在某處將父線程中的ThreadLocal值賦值到了子線程的inheritableThreadLocals中。
step2:在源代碼中搜索哪些地方使用到了inheritableThreadLocals
這個屬性,最後找到這段代碼:
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屬性是否爲空,不爲空的話就複製給子線程 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(); }
上面的代碼印證了咱們的猜測。須要注意的是一旦子線程被建立之後,再操做父線程中的ThreadLocal變量,那麼子線程是不能感知的。由於父線程和子線程仍是擁有各自的ThreadLocalMap,只是在建立子線程的「一剎那」將父線程的ThreadLocalMap複製給子線程,後續二者就沒啥關係了。
原文出處:https://www.cnblogs.com/54chensongxia/p/12015443.html