java併發:lnheriitableThreadLocal
初識lnheriitableThreadLocal
lnheriitableThreadLocal繼承自 ThreadLocal,其提供了一個特性,讓子執行緒可以訪問在父執行緒中設定的本地變數。
程式碼示例
public static void main(String[] args) { ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>(){ @Override protected Integer initialValue() { return0; } }; threadLocal.set(1); Thread newThread = new Thread() { @Override public void run() { System.out.println(threadLocal.get()); } }; newThread.start(); }
上面這段程式碼,使得newThread訪問到了main方法所線上程中的threadLocal變數的變動,即此處newThread將輸出1。
請對比下面這段程式碼:
publicstatic void main(String[] args) { ThreadLocal<Integer> threadLocal = new ThreadLocal<>(){ @Override protected Integer initialValue() { return 0; } }; threadLocal.set(1); Thread newThread = new Thread() { @Override public void run() { System.out.println(threadLocal.get()); } }; newThread.start(); }
此段程式碼與第一個例子不同的地方在於threadLocal變數的型別不一樣,此處newThread將輸出0。
原始碼分析
此處將跟蹤Thread的建立過程,以搞明白為什麼lnheriitableThreadLocal型別的變數可以暴露在新建立的執行緒中。
/** * Allocates a new {@code Thread} object. This constructor has the same * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread} * {@code (null, target, gname)}, where {@code gname} is a newly generated * name. Automatically generated names are of the form * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer. * * @param target * the object whose {@code run} method is invoked when this thread * is started. If {@code null}, this classes {@code run} method does * nothing. */ public Thread(Runnable target) { this(null, target, "Thread-" + nextThreadNum(), 0); }
public Thread(ThreadGroup group, Runnable target, String name, long stackSize) { this(group, target, name, stackSize, null, true); }
/** * Initializes a Thread. * * @param g the Thread group * @param target the object whose run() method gets called * @param name the name of the new Thread * @param stackSize the desired stack size for the new thread, or * zero to indicate that this parameter is to be ignored. * @param acc the AccessControlContext to inherit, or * AccessController.getContext() if null * @param inheritThreadLocals if {@code true}, inherit initial values for * inheritable thread-locals from the constructing thread */ private Thread(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) { if (name == null) { throw new NullPointerException("name cannot be null"); } this.name = name; Thread parent = currentThread(); SecurityManager security = System.getSecurityManager(); if (g == null) { /* Determine if it's an applet or not */ /* If there is a security manager, ask the security manager what to do. */ if (security != null) { g = security.getThreadGroup(); } /* If the security manager doesn't have a strong opinion on the matter, use the parent thread group. */ if (g == null) { g = parent.getThreadGroup(); } } /* checkAccess regardless of whether or not threadgroup is explicitly passed in. */ g.checkAccess(); /* * Do we have the required permissions? */ if (security != null) { if (isCCLOverridden(getClass())) { security.checkPermission( SecurityConstants.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); if (inheritThreadLocals && 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 */ this.tid = nextThreadID(); }
上述程式碼中著色部分是關鍵,至此將跳轉到ThreadLocal中,程式碼如下:
/** * Factory method to create map of inherited thread locals. * Designed to be called only from Thread constructor. * * @param parentMap the map associated with parent thread * @return a map containing the parent's inheritable bindings */ static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) { return new ThreadLocalMap(parentMap); }
上述程式碼呼叫了ThreadLocalMap的建構函式,程式碼如下:
/** * Construct a new map including all Inheritable ThreadLocals * from given parent map. Called only by createInheritedMap. * * @param parentMap the map associated with parent thread. */ private ThreadLocalMap(ThreadLocalMap parentMap) { Entry[] parentTable = parentMap.table; int len = parentTable.length; setThreshold(len); table = new Entry[len]; for (Entry e : parentTable) { if (e != null) { @SuppressWarnings("unchecked") ThreadLocal<Object> key = (ThreadLocal<Object>) e.get(); if (key != null) { Object value = key.childValue(e.value); Entry c = new Entry(key, value); int h = key.threadLocalHashCode & (len - 1); while (table[h] != null) h = nextIndex(h, len); table[h] = c; size++; } } } }
上述程式碼中著色部分呼叫了ThreadLocal中的childValue方法,該方法的程式碼如下:
/** * Method childValue is visibly defined in subclass * InheritableThreadLocal, but is internally defined here for the * sake of providing createInheritedMap factory method without * needing to subclass the map class in InheritableThreadLocal. * This technique is preferable to the alternative of embedding * instanceof tests in methods. */ T childValue(T parentValue) { throw new UnsupportedOperationException(); }
上述方法由lnheriitableThreadLocal重寫,與此同時,lnheriitableThreadLocal重寫了getMap和createMap方法,整體程式碼如下:
public class InheritableThreadLocal<T> extends ThreadLocal<T> { /** * Computes the child's initial value for this inheritable thread-local * variable as a function of the parent's value at the time the child * thread is created. This method is called from within the parent * thread before the child is started. * <p> * This method merely returns its input argument, and should be overridden * if a different behavior is desired. * * @param parentValue the parent thread's value * @return the child thread's initial value */ protected T childValue(T parentValue) { return parentValue; } /** * Get the map associated with a ThreadLocal. * * @param t the current thread */ ThreadLocalMap getMap(Thread t) { return t.inheritableThreadLocals; } /** * Create the map associated with a ThreadLocal. * * @param t the current thread * @param firstValue value for the initial entry of the table. */ void createMap(Thread t, T firstValue) { t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue); } }
總結:
執行緒在通過 InheritableThreadLocal類例項的 set或者 get方法設定變數時,會建立當前執行緒的 inheritableThreadLocals變數。
InheritableThreadLocal類通過重寫程式碼,讓本地變數儲存到了具體執行緒的 inheritableThreadLocals變數裡面。
當父執行緒建立子執行緒時,建構函式會把父執行緒中 inheritableThreadLocals變數裡面的本地變數複製一份儲存到子執行緒的 inheritableThreadLocals 變數裡面。
思考:
在什麼情況下子執行緒需要獲取父執行緒的 threadlocal 變數呢,實現方式有哪些?
最簡單的實現方式:建立執行緒時傳入父執行緒中的變數,將其複製到子執行緒中