【每日一学之InheritableThreadLocal原理分析】

1,ThreadLocal的使用与缺点

ThreadLocal可以保证每个线程获取到的ThreadLocal变量是相互隔离的,我在线程1中获取ThreadLocal对象并set一个值,在线程2中获取的ThreadLocal的对象无法获取到在线程1中set的值,demo如下:

public class MyStudy1014_01 implements Runnable{
    private static ThreadLocal<String> mThreadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        mThreadLocal.set("主线程mThreadLocal有值了");
        System.out.println("获取主线程mThreadLocal中的值 : "+mThreadLocal.get());
        Thread t1 = new Thread(new MyStudy1014_01());
        t1.start();
    }

    @Override
    public void run() {
        System.out.println("子线程中获取mThreadLocal的值"+mThreadLocal.get());
    }
}
    }
}

运行结果如下, 子线程中获取的ThreadLocal中的值为空

在这里插入图片描述

2,InheritableThreadLocal弥补ThreadLocal的缺点

上面的案例中,我们将ThreadLocal换成InheritableThreadLocal,再次执行

public class MyStudy1014_02 implements Runnable{

    private static InheritableThreadLocal<String> mInheritableThreadLocal = new InheritableThreadLocal<>();

    public static void main(String[] args) {
        mInheritableThreadLocal.set("主线程mInheritableThreadLocal有值了");
        System.out.println("获取主线程mInheritableThreadLocal中的值 : "+mInheritableThreadLocal.get());
        Thread t1 = new Thread(new MyStudy1014_02());
        t1.start();
    }

    @Override
    public void run() {
        System.out.println("子线程中获取mInheritableThreadLocal的值"+mInheritableThreadLocal.get());
    }
}

执行结果如下,可以看到,子线程获取的InheritableThreadLocal 对象可以get到在主线程中set的值
在这里插入图片描述

3,InheritableThreadLocal弥补ThreadLocal的缺点的原理

使用idea快捷键alt + 7 查看InheritableThreadLocal的类结构,发现它只重写了ThreadLocal的childValue(T), getMap(T)以及createMap(Thread,T)3个方法
在这里插入图片描述
对比InheritableThreadLocal和ThreadLocal源码


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);
    }
}
   T childValue(T parentValue) {
        throw new UnsupportedOperationException();
    }
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }   

我们都知道ThreadLocal在set以及get时,实际执行的是当前Thread对象中ThreadLocalMap的方法,这里可参照ThreadLocal的源码也可参照这篇博客(点此跳转)。所以问题出在getMap的返回值,可以看到ThreadLocal返回的是Thread.threadLocals, 而InheritableThreadLocal返回的是Thread.inheritableThreadLocals。继续查看Thread下这两个变量的区别。

 private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
       if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
       
    }

可以发现当Thread对象初始化的时候,注意这个时候还在主线程进行对象的初始化,会将主线程的inheritableThreadLocals对象赋值给新创建的Thread对象中的inheritableThreadLocals变量,所以当Thread.start()的时候,会拿到父线程也就是主线程mInheritableThreadLocal对象中的值

4,注意,由于拷贝父线程中mInheritableThreadLocal对象发生在Thread对象初始化时期,所以线程池与mInheritableThreadLocal搭配通常是不好用的,因为线程池复用技术不会初始化Thread对象

实例如下:

public class MyStudy1014_03 {

    private static InheritableThreadLocal<String> mInheritableThreadLocal = new InheritableThreadLocal<>();
    private static ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(1);

    public static void main(String[] args) {
        mInheritableThreadLocal.set("主线程第一次set值");
        System.out.println("主线程mInheritableThreadLocal第一次get"+mInheritableThreadLocal.get());
        newFixedThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("子线程mInheritableThreadLocal第一次get"+mInheritableThreadLocal.get());
            }
        });
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        mInheritableThreadLocal.set("主线程第二次set值");
        System.out.println("主线程mInheritableThreadLocal第二次get"+mInheritableThreadLocal.get());
        newFixedThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("子线程mInheritableThreadLocal第二次get"+mInheritableThreadLocal.get());
            }
        });
        newFixedThreadPool.shutdown();
    }
}

第二次线程池获取mInheritableThreadLocal中的值仍然是第一次中的值
第二次线程池获取mInheritableThreadLocal中的值仍然是第一次中的值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值