【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递

从源码看InheritableThreadLocal和TTL父子线程传递

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD

🔥 2025本人正在沉淀中… 博客更新速度++

👍 欢迎点赞、收藏、关注,跟上我的更新节奏

📚欢迎订阅专栏,专栏名《在2B工作中寻求并发是否搞错了什么》

本文涉及InheritableThreadLocal和TTL,从源码的角度,分别分析它们是怎么实现父子线程传递的。

建议先了解ThreadLocal。

【源码】【Java并发】【ThreadLocal】适合中学者体质的ThreadLocal源码阅读

【Java并发】【ThreadLocal】适合初学体质的ThreadLocal

在这里插入图片描述

ThreadLocal存在的问题

众所周知,ThreadLocal并没有解决父子间线程传递的问题,比如下面的代码。

public static void main(String[] args) throws InterruptedException {
   
   
    ThreadLocal<String> threadLocal = new ThreadLocal<>();
    // threadLocal set值
    threadLocal.set("main thread info");
    System.out.println("main thread get: " + threadLocal.get());
    // 启动子线程
    new Thread(() -> System.out.println("sub thread get: " + threadLocal.get())).start();
    threadLocal.remove();
}

输出结果

main thread get: main thread info
sub thread get: null

InheritableThreadLocal实现父子线程传递

ThreadLocal换为InheritableThreadLocal:

public static void main(String[] args) throws InterruptedException {
   
   
    InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
    // threadLocal set值
    threadLocal.set("main thread info");
    System.out.println("main thread get: " + threadLocal.get());
    // 启动子线程
    new Thread(() -> System.out.println("sub thread get: " + threadLocal.get())).start();
    threadLocal.remove();
}

输出结果

main thread get: main thread info
sub thread get: main thread info

🤔,那我们什么我换成InheritableThreadLocal就可以了呢?

省流版:原理就是,主线程创建子线程的时候,子线程会拷贝1份主线程的ThreadLocalMap

在这里插入图片描述

当我们InheritableThreadLocalset值的时候:

初始化当前线程的ThreadLocalMapInheritableThreadLocal会,new一个ThreadLocalMap,赋值给当前线程的inheritableThreadLocals字段。(而ThreadLocal的逻辑是,new一个ThreadLocalMap,赋值给当前线程的threadLocals字段)

// ThreadLocal#set
public void set(T value) {
   
   
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
   
   
        map.set(this, value);
    } else {
   
   
        // 👇初始化ThreadLocalMap,createMap调用子类InheritableThreadLocal类的实现
        createMap(t, value);	
    }
}

// InheritableThreadLocal#createMap
void createMap(Thread t, T firstValue) {
   
   
    // 当前线程的inheritableThreadLocals字段
    t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}

// ThreadLocal#createMap
void createMap(Thread t, T firstValue) {
   
   
    // 当前线程的threadLocals字段
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

聪明的你一定会问了,主播,主播,赋值给inheritableThreadLocals有什么用呢?

那这可是大有说法的,让我们从Thread类开始说起,当我们在主线程,new Thread()创建子线程的时候,Thread类的init方法里有个if判断:

  • 我们重点看,parent.inheritableThreadLocals != null ,因为inheritableThreadLocals我们在set的时候已经赋值了,所以这里为true。
  • inheritThreadLocalsinit方法的入参,我们很少使用到,这样的方式创建线程Thread(Runnable target, AccessControlContext acc),所以大部分情况都是true。

如果这两个条件都为true的话,那么会将主线程的ThreadLocalMap数据拷贝到子线程的inheritableThreadLocals里。

ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize) {
   
   
    init(g, target, name, stackSize, null, true);
}

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
   
   
        ...
        Thread parent = currentThread();
        ...
        // 重点在这里,将主线程的ThreadLocalMap拷贝到子线程的inheritableThreadLocals里。
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        ...

createInheritedMap将主线程的ThreadLocalMap拷贝到子线程里。

static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
   
   
    return new ThreadLocalMap(parentMap);
}

// 将主线程的ThreadLocalMap数据拷贝到子线程里
private ThreadLocalMap(ThreadLocalMap parentMap) {
   
   
    Entry[] parentTable = parentMap.table;
    int len = parentTable.length;
    setThreshold(len);
    table = new Entry[len];

    for (int j = 0; j < len; j++) {
   
   
        Entry e = parentTable[j];
        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
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值