ThreadLocal MDC
因为MDC底层是用ThreadLocal实现的,所以这里补充一些和ThreadLocal相关的知识点。
1.ThreadLocal的三个层次
关于ThreadLocal有三个层次,可以按照这三个层次去理解就不会乱。
三个层次
* 第一层是Thread空间,通过Thread.currentThread()获得。
* 第二层是Thread中的两个ThreadLocalMap,threadLocals和inheritableThreadLocals,访问thread对应的两个ThreadLocalMap成员变量获得。
* 第三层是每个ThreadLocalMap中key——ThreadLocal和value——ThreadLocal的set方法set的值,在get方法中用ThreadLocal的this作为ThreadLocalMap的key获取value。
* 无论什么操作都要按照这三个层次依次进行才不会乱
2.ThreadLocalMap
保存了当前Thread中存放的ThreadLocal和ThreadLocal对应的值的键值对。
当前线程的所有ThreadLocal变量组成了这个map的keyset,对应的值组成了这个map的valueset。
3.ThreadLocal的操作
第一步都是先用Thread.currentThread()获得当前线程,然后getMap获取线程中的ThreadLocalMap。
像getMap这些操作方法都是包可见性的,包外部无法操作。以ThreadLocal的get方法举例,
- /**
- * Returns the value in the current thread's copy of this
- * thread-local variable. If the variable has no value for the
- * current thread, it is first initialized to the value returned
- * by an invocation of the {@link #initialValue} method.
- *
- * @return the current thread's value of this thread-local
- */
- public T get() {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null) {
- ThreadLocalMap.Entry e = map.getEntry(this);
- if (e != null)
- return (T)e.value;
- }
- return setInitialValue();
- }
- /**
- * Returns the value in the current thread's copy of this
- * thread-local variable. If the variable has no value for the
- * current thread, it is first initialized to the value returned
- * by an invocation of the {@link #initialValue} method.
- *
- * @return the current thread's value of this thread-local
- */
- public T get() {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null) {
- ThreadLocalMap.Entry e = map.getEntry(this);
- if (e != null)
- return (T)e.value;
- }
- return setInitialValue();
- }
4.InheritableThreadLocal
Thread类中有两个ThreadLocalMap,一个是threadLocals,一个是inheritableThreadLocals。
threadLocals保存的是当前线程中的ThreadLocal变量们,inheritableThreadLocals保存的是当前线程父线程中的变量们。
InheritableThreadLocal类覆写了getMap和createMap这两个方法,
- /**
- * Get the map associated with a ThreadLocal.
- *
- * @param t the current thread
- */
- ThreadLocalMap getMap(Thread t) {
- return t.inheritableThreadLocals;
- }
- /**
- * 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.
- * @param map the map to store.
- */
- void createMap(Thread t, T firstValue) {
- t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
- }
- /**
- * Create the map associated with a ThreadLocal.
- *
- * @param t the current thread
- * @param firstValue value for the initial entry of the table.
- * @param map the map to store.
- */
- void createMap(Thread t, T firstValue) {
- t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
- }
5.threadLocals和inheritableThreadLocals的初始化
- /**
- * 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.
- */
- private void init(ThreadGroup g, Runnable target, String name,
- long stackSize) {
- ...
- Thread parent = currentThread();
- ...
- if (parent.inheritableThreadLocals != null)
- this.inheritableThreadLocals =
- ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
- ...
- }
- /**
- * 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.
- */
- private void init(ThreadGroup g, Runnable target, String name,
- long stackSize) {
- ...
- Thread parent = currentThread();
- ...
- if (parent.inheritableThreadLocals != null)
- this.inheritableThreadLocals =
- ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
- ...
- }
注意:该操作只在线程初始化的时候进行,所以在该线程初始化之后,parent线程对parent线程自己的inheritableThreadLocals变量的操作不会影响到当前线程的inheritableThreadLocals了,因为已经不是同一个map了。
MDC就是利用这个InheritableThreadLocal把父线程的context带到子线程中,把上下文传递到子线程中通过日志输出,把一次完整的请求串联起来。
6.parent线程
- Thread parent = currentThread();
- Thread parent = currentThread();
但是init方法只在线程初始化的时候执行一次,所以如果用的线程池来使线程重用的话,就不会再调用这个init方法了,这会带来一些问题,后面会具体说。