聊聊InheritableThreadLocal实现变量值传递的原理

特点:可以实现父子线程的变量值传递,对于线程池,就没法传递了(因为线程池中存在线程复用的情况,并不会随着调用一直创建,如果要在线程池中实现变量传递,可以使用阿里的TTL线程池)

原理:线程在创建的时候会执行init(),将父线程的inheritableThreadLocal拷贝过来,进而实现变量值传递的目的

以下的具体的源码分析:

  1. 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);
    }
}
  1. Thread线程创建时会调用init方法,该方法内会将父线程的inheritableThreadLocalMap进行拷贝,从而子线程能获取父线程传递的变量值
private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
    // ...

    // 因为子线程是父线程创建的,所以currentThread拿到的就是父线程的信息
    Thread parent = currentThread();
    // ...
    
    // 如果父线程的inheritableThreadLocals不为空,则创建一个threadLocalMap,将父线程threadLocalMap的数据复制到子线程中
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);   
}


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

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) {
                // 这里就是把父线程的threadLocalMap中的kv数据填充到子线程中
                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] = c;
                size++;
            }
        }
    }
}

// inheritableThreadLocal直接把值返回,没有做特殊的深拷贝处理
protected T childValue(T parentValue) {
    return parentValue;
}

注意:

inheritableThreadLocal父线程值传递的时候是直接把变量赋值给子线程的,如果值是引用类型,那么子线程的修改会导致父线程的值也发生变更的;如果是普通类型的变量,则可以认为数据是相互隔离的

实例demo:

public class InheritableDemo1 {
    static InheritableThreadLocal<Man> itl = new InheritableThreadLocal<>();

    static InheritableThreadLocal<String> itl1 = new InheritableThreadLocal<>();


    public static void main(String[] args) throws InterruptedException {
        Man man = new Man();
        man.setAge(18);
        man.setName("张三");
        itl.set(man);
        itl1.set("hello");
        System.out.println("修改前" + Thread.currentThread().getName() + "线程对应的itl:" + itl.get());
        System.out.println("修改前" + Thread.currentThread().getName() +"线程对应的itl1:" + itl1.get());

        // 创建子线程
        new Thread(() -> {
            Man man1 = itl.get();
            System.out.println("子线程修改前itl:" + Thread.currentThread().getName() + ":" + itl.get());
            System.out.println("子线程修改前itl1:" + Thread.currentThread().getName() + ":" + itl1.get());
            man1.setName("李四");
            itl1.set("world");
            System.out.println("子线程修改后itl:" + Thread.currentThread().getName() + ":" + itl.get());
            System.out.println( "子线程修改后itl1:" + Thread.currentThread().getName() + ":" + itl1.get());
        }).start();

        Thread.sleep(1000);

        // inheritableThreadLocal父线程值传递的时候是直接把变量赋值给子线程的,如果值是引用类型,那么子线程的修改会导致父线程的值也发生变更的
        System.out.println("修改后" + Thread.currentThread().getName() + "线程对应的itl:" + itl.get());
        System.out.println("修改后" +Thread.currentThread().getName() +"线程对应的itl1:" + itl1.get());

        // 修改前main线程对应的itl:Man(name=张三, age=18)
        // 修改前main线程对应的itl1:hello
        // 子线程修改前itl:Thread-0:Man(name=张三, age=18)
        // 子线程修改前itl1:Thread-0:hello
        // 子线程修改后itl:Thread-0:Man(name=李四, age=18)
        // 子线程修改后itl1:Thread-0:world
        // 修改后main线程对应的itl:Man(name=李四, age=18)
        // 修改后main线程对应的itl1:hello
    }

}

因为子线程的修改,父线程的ThreadLocal值已发生变更

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值