关于inheritableThreadLocals的关键逻辑及坑

介绍

inheritableThreadLocals是一个线程变量,继承于ThreadLocal。

重写了3个方法,特别是下面的两个方法。

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);
}

从上述可知,它在获取Map集合的时候用的线程成员变量inheritableThreadLocals。 而父类ThreadLocal用的Map集合是线程成员变量threadLocals.

这两个变量都是属于线程成员变量,初始值为空。

  /* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

/*
 * InheritableThreadLocal values pertaining to this thread. This map is
 * maintained by the InheritableThreadLocal class.
 */
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

这两个成员变量的区别在于,threadLocals 不能被子线程继承,inheritableThreadLocals 可以被子线程继承。这一点可以在创建线程的时候看到。

public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
}

公开访问的两个创建线程的方法中都调用了init方法,关键代码如下:

....
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 */
    tid = nextThreadID();

如果parent.inheritableThreadLocals != null,那么执行 this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);以此方式将线程变量集合Map传递给子线程。

从上述可知,集合Map是通过线程创建过程传递的,当业务中存在异步调用时注意了:
1. 如果子线程直接由父线程创建后再使用,那么子线程可以得到父线程的集合Map,父线程中通过inheritableThreadLocals.set()的值也可以传递到子线程。
2. 如果子线程不是由当前父线程直接创建的,而是已经存在的,那么无法传递。比如线程池使用时,线程的循环使用过程中就存在这种情况,子线程得不到父线程通过inheritableThreadLocals.set()的值,因为子线程是被其他线程创建后再循环使用的。因此线程池中不建议使用inheritableThreadLocals, 直接new Thread(Runnable);
3. remove()方法,父子线程都使用了inheritableThreadLocals,那么谁需要调用remove(),从而避免内存泄漏?都需要调用。

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值