最近项目中为了让打印的日志能够追踪,看到了Logback和log4j2支持的MDC功能,其内部利用了子线程从父线程继承InheritableThreadLocal类型变量的特性。之前只使用到了ThreadLocal,在这里作一下对比,并简单的作了一些分析java
ThreadLocal声明的变量是线程私有的成员变量,每一个线程都有该变量的副本,线程对变量的修改对其余线程不可见数组
InheritableThreadLocal声明的变量一样是线程私有的,可是子线程可使用一样的InheritableThreadLocal类型变量从父线程继承InheritableThreadLocal声明的变量,父线程没法拿到其子线程的。即便能够继承,可是子线程对变量的修改对父线程也是不可见的。ide
Thread有一个类型为ThreadLocal.ThreadLocalMap变量:threadLocals,ThreadLocalMap底层是一个Entry数组,用于保存ThreadLocal引用和真正变量(程序中销售人的主管姓名)的值测试
看下面的代码和注释了解如何将值放入threadLocals的this
调用ThreadLocal对象的set()方法spa
public void set(T value) { //获取到当前线程对象 Thread t = Thread.currentThread(); //从当前线程对象中拿threadLocals变量 ThreadLocalMap map = getMap(t); //若是threadLocals不为空,则调用它的set方法,将key=当前ThreadLocal对象 value=主管姓名 if (map != null) map.set(this, value); //若是threadLocals为空,则新建立一个ThreadLocalMap else createMap(t, value); }
InheritableThreadLocal继承自ThreadLocal,所以能够想到它本身要么重写,要么新增了部分方法。从源码看只是重写了childValue、getMap、createMap方法,其中getMap和createMap都是父类set方法中使用到的,所以主要是改变了父类set方法的行为。线程
getMap获取的是InheritableThreadLocal类型的变量日志
createMap建立的是InheritableThreadLocal类型的实例对象
可是从这点上看是不能达到子线程继承父线程的变量的。再看看Thread的init方法中比之前新增了以下代码:blog
//若是当前线程的InheritableThreadLocal类型变量inheritThreadLocals为空且父线程的 //inheritThreadLocals不为空,则复制父线程的inheritThreadLocals中的内容给当前线程的 //inheritThreadLocals,是值复制不是引用赋值 if (inheritThreadLocals && parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
上面说到重写了三个方法,其中的childValue尚未找到使用的地方,如今该它出场了,就是在继承变量的时候,建立新的ThreadLocalMap使用到了。
createInheritedMap->ThreadLocalMap->{
//建立一个新的Entry数组
table = new Entry[len];
//复制数据
Object value = key.childValue(e.value); Entry c = new Entry(key, value); table[h] = c;
}
写一个销售人类,该类有一个主管人员成员变量(ThreadLocal声明),将销售人对象传给不一样的工做线程,在工做线程中打印销售人的主管,其结果应该是为NULL,由于主管是声明为线程本地变量,是不能共享的
package com.jv.jdk.thread.threadlocal; import lombok.Data; @Data public class Salesman { private ThreadLocal<String> charger = new ThreadLocal<>(); public Salesman(String charger){ this.charger.set(charger); } }
package com.jv.jdk.thread.threadlocal; import lombok.extern.log4j.Log4j2; //@Log4j2注解能够很方便作日志输出 import java.util.concurrent.CountDownLatch; @Log4j2 public class Worker implements Runnable{ private Salesman salesman = null; private CountDownLatch countDownLatch = null; public Worker(Salesman salesman, CountDownLatch countDownLatch){ this.salesman = salesman; this.countDownLatch = countDownLatch; } @Override public void run() { log.info(Thread.currentThread().getName()+":"+salesman.getCharger().get()); countDownLatch.countDown(); } }
测试类
@Test public void threadLocal(){ CountDownLatch countDownLatch = new CountDownLatch(2); Salesman salesman = new Salesman("刘墙东"); Thread thread_1 = new Thread(new Worker(salesman,countDownLatch)); Thread thread_2 = new Thread(new Worker(salesman,countDownLatch)); thread_1.start(); thread_2.start(); try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } }
测试结果:
2018.08.30 at 14:47:45 CST 14-Thread-1 INFO com.jv.jdk.thread.threadlocal.Worker run() @20 - Thread-1:null 2018.08.30 at 14:47:45 CST 15-Thread-2 INFO com.jv.jdk.thread.threadlocal.Worker run() @20 - Thread-2:null
与上面的代码重写SalesMan类
package com.jv.jdk.thread.threadlocal; import lombok.Data; @Data public class SalesmanInherit extends Salesman{ private InheritableThreadLocal<String> charger = new InheritableThreadLocal<>(); public SalesmanInherit(String charger){ super(charger); this.charger.set(charger); } }
@Test public void inheritablThreadLocal(){ CountDownLatch countDownLatch = new CountDownLatch(2); Salesman salesman = new Salesman("刘墙东"); SalesmanInherit salesmanInherit = new SalesmanInherit("刘墙东"); Thread thread_1 = new Thread(new Worker(salesmanInherit,countDownLatch)); Thread thread_2 = new Thread(new Worker(salesmanInherit,countDownLatch)); thread_1.start(); thread_2.start(); try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } }
测试结果:
2018.08.30 at 14:51:28 CST 15-Thread-2 INFO com.jv.jdk.thread.threadlocal.Worker run() @20 - Thread-2:刘墙东 2018.08.30 at 14:51:28 CST 14-Thread-1 INFO com.jv.jdk.thread.threadlocal.Worker run() @20 - Thread-1:刘墙东