ThreadLocal和InheritableThreadLocal实现原理

ThreadLocal实现原理

// ThreadLocal.set()源码
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}



//ThreadLocalMap保存在Thread中:
 
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

可见threadLocals为Thread的内部变量,每个Thread类的实例,都有自己的threadLocals变量

// Thread类源码
public class Thread implements Runnable {
  	......
    ThreadLocal.ThreadLocalMap threadLocals = null;
    ......
}

总结
ThreadLocal属性保存在每个线程实例中,线程实例通过Map结构管理所有的ThreadLocal属性,Map的key为ThreadLocal属性。

InheritableThreadLocal实现原理

ThreadLocal属性是Thread的内部变量,父子线程是不同的线程实例,显然在子线程中无法获取父线程设置的属性:

public class MainToChild implements Runnable {
 
    public static ThreadLocal<String> threadLocal = new ThreadLocal();
 
    @Override
    public void run() {
        System.out.println("child : " + Thread.currentThread().getName());
        System.out.println("child : " + threadLocal.get());
    }
 
    public static void main(String[] args) {
 
        MainToChild mainToChild = new MainToChild();
        Thread thread = new Thread(mainToChild);
        thread.start();
 
        threadLocal.set("main");
 
        System.out.println("main : " + Thread.currentThread().getName());
        System.out.println("main : " + threadLocal.get());
    }
}

---------------------------------------------
main : main
child : Thread-0
child : null
main : main

ThreadLocal属性是Thread的内部变量,父子线程是不同的线程实例,显然在子线程中无法获取父线程设置的属性

InheritableThreadLocal实现父子进程传递

public class MainToChild implements Runnable {
 
    public static InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal();
 
    @Override
    public void run() {
        System.out.println("child name : " + Thread.currentThread().getName());
        System.out.println("child get main value : " + threadLocal.get());
        threadLocal.set("child");
        System.out.println("child value : " + threadLocal.get());
 
    }
 
    public static void main(String[] args) {
 
        threadLocal.set("main");
 
        MainToChild mainToChild = new MainToChild();
        Thread thread = new Thread(mainToChild);
        thread.start();
 
        System.out.println("main name : " + Thread.currentThread().getName());
        System.out.println("main value: " + threadLocal.get());
    }
}




---------------------------------------------
main name : main
child name : Thread-0
child get main value : main
main value: main
child value : child

InheritableThreadLocal改变初始值后子进程无法获取新值

package com.sankuai.mall.consumerapi.web.controller;
 
import lombok.SneakyThrows;
 
/**
 * @author : mazhen
 * Date    : 2021/11/12
 * Time    : 3:11 下午
 * ---------------------------------------
 * Desc    :
 */
public class MainToChild implements Runnable {
 
    public static InheritableThreadLocal threadLocal = new InheritableThreadLocal();
 
    @SneakyThrows
    @Override
    public void run() {
        System.out.println("child name : " + Thread.currentThread().getName());
        System.out.println("1 child get main value : " + threadLocal.get());
        System.out.println("2 child get main value : " + threadLocal.get());
    }
 
    public static void main(String[] args) {
 
        threadLocal.set("main");
 
        MainToChild mainToChild = new MainToChild();
        Thread thread = new Thread(mainToChild);
        thread.start();
 
        threadLocal.set("again main");
 
        System.out.println("main name : " + Thread.currentThread().getName());
        System.out.println("main value: " + threadLocal.get());
    }
 
}


子进程无法获取新值
---------------------------------------------
child name : Thread-0
main name : main
1 child get main value : main
main value: again main
2 child get main value : main

改为线程池的方式也是一样的效果

package com.sankuai.mall.consumerapi.web.controller;
 
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
/**
 * @author : mazhen
 * Date    : 2021/11/12
 * Time    : 4:11 下午
 * ---------------------------------------
 * Desc    :
 */
public class ITL {
 
    public static InheritableThreadLocal<String> param = new InheritableThreadLocal<>();
 
    public static void main(String[] args) {
        param.set("main");
 
        ExecutorService pool = Executors.newFixedThreadPool(1);
        pool.submit(() -> {
            System.out.println(param.get());
        });
        param.set("main2");
        pool.submit(() -> {
            System.out.println(param.get());
        });
        pool.submit(() -> {
            System.out.println(param.get());
        });
        pool.shutdown();
    }
 
}



线程池只能获取初始值,无法获取新值
---------------------------------------------
main
main
main

InheritableThreadLocal 实现原理

// Thread类源码
public class Thread implements Runnable {
  	......
	ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    ......
}

数据结构ThreadLocalMap和threadLocal是一样的

InheritableThreadLocal及其局限性
Jdk提供了InheritableThreadLocal类,用于在父子线程间传递线程变量(ThreadLocal),实现原理就是在Thread类保存名为inheritableThreadLocals的成员属性(以InheritableThreadLocal对象为Key的ThreadLocalMap),并在初始化创建子线程时,将父线程的inheritableThreadLocals赋给子线程,这部分逻辑在Thread.init()方法内。

//只会在初始化一次,设置为父类的变量值
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);
  		.....
}
 
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
}

//初始化时会把父类的线程变量遍历的拷贝过来
private ThreadLocalMap(ThreadLocalMap parentMap) {
       Entry[] parentTable = parentMap.table;//父类的table
       int len = parentTable.length;
       setThreshold(len);
       table = new Entry[len];//当前类的table长度和父类相同
 
       for (int j = 0; j < len; j++) {
           Entry e = parentTable[j]; //遍历父类table
           if (e != null) {
               @SuppressWarnings("unchecked")
               ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
               if (key != null) {
                    // 需要注意childValue方法,通过重写这个方法,
                    // 可以实现自定义的拷贝逻辑
                    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++;
               }
           }
       }
}

这种方式在线程只被创建和使用一次时是有效的,但对于使用线程池的场景下,由于线程被复用,初始化一次后,后续修改变量,子线程并不会再更新初始值,导致后续提交的任务并不会获取到父线程的线程变量最新值,同时,还会获取到当前线程之前的初始变量值。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值