IE盒子

搜索
查看: 150|回复: 0

Java多线程之InheritableThreadLocal类

[复制链接]

2

主题

7

帖子

12

积分

新手上路

Rank: 1

积分
12
发表于 2023-1-18 09:55:02 | 显示全部楼层 |阅读模式
针对ThreadLocal不可继承的特点,Java还引入了InheritableThreadLocal类,顾名思义其是可以继承的



abstract.jpeg

基本实践

ThreadLocal的不可继承性指的是子线程无法继承获取到父线程中的值。而InheritableThreadLocal则通过继承ThreadLocal类实现了可继承性。测试代码如下所示
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class InheritableThreadLocalDemo {

    /**
     * ThreadLocal继承性测试
     */
    @Test
    public void test1() {
        System.out.println("\n--------------------------- Test 1 ---------------------------");
        ThreadLocal<String> userInfo = new ThreadLocal<>();
        userInfo.set("Aaron");
        new Thread( ()-> System.out.println("<子线程> userInfo: " + userInfo.get()) )
            .start();
        System.out.println("<父线程> userInfo: " + userInfo.get());
    }

    /**
     * InheritableThreadLocal继承性测试
     */
    @Test
    public void test2() {
        System.out.println("\n--------------------------- Test 2 ---------------------------");
        InheritableThreadLocal<String> userInfo = new InheritableThreadLocal<>();
        userInfo.set("Aaron");
        new Thread( ()-> System.out.println("<子线程> userInfo: " + userInfo.get()) )
            .start();
        System.out.println("<父线程> userInfo: " + userInfo.get());
    }

}
测试结果如下,符合预期



figure 1.jpeg

基本原理

其基本原理也很简单,该类继承了ThreadLocal并重写了下述的三个方法。众所周知,ThreadLocal是通过Thread类中ThreadLocal.ThreadLocalMap类型的threadLocals字段实现线程本地存储的。而InheritableThreadLocal则是使用Thread类中ThreadLocal.ThreadLocalMap类型的inheritableThreadLocals字段。其实现如下
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);
    }
}
而其继承性的实现也很简单,即在子线程创建阶段通过init方法将将父线程的inheritableThreadLocals浅拷贝到子线程中,Thread实例创建过程中相关源码部分实现如下
public class Thread implements Runnable {

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

    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();
    ...
    // 父线程的inheritableThreadLocals变量不为空
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
     // 将父线程的inheritableThreadLocals浅拷贝到子线程
        this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    ...
    }

}
前面提到子线程虽然继承了父线程的值,但其是通过浅拷贝的方式进行的。下面通过示例代码来验证下
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class InheritableThreadLocalDemo {

   @Test
    public void test3() throws Exception {
        System.out.println("\n--------------------------- Test 3 ---------------------------");
        InheritableThreadLocal<String> userInfo = new InheritableThreadLocal<>();
        userInfo.set("Aaron");
        System.out.println("<父线程> userInfo #1: " + userInfo.get());
        Thread thread = new Thread( ()-> {
            System.out.println("<子线程> userInfo #2: " + userInfo.get());
            userInfo.set("Bob");
            System.out.println("<子线程> userInfo #3: " + userInfo.get());
        } );
        thread.start();
        thread.join();
        System.out.println("<父线程> userInfo #4: " + userInfo.get());
    }

    @Test
    public void test4() throws Exception {
        System.out.println("\n--------------------------- Test 4 ---------------------------");
        InheritableThreadLocal<User> userInfo = new InheritableThreadLocal<>();
        userInfo.set( new User("Aaron") );
        System.out.println("<父线程> userInfo #1: " + userInfo.get());
        Thread thread = new Thread( ()-> {
            System.out.println("<子线程> userInfo #2: " + userInfo.get());
            userInfo.get().setName("Bob");
            System.out.println("<子线程> userInfo #3: " + userInfo.get());
        } );
        thread.start();
        thread.join();
        System.out.println("<父线程> userInfo #4: " + userInfo.get());
    }

    @AllArgsConstructor
    @Data
    private static class User {
        private String name;
    }

}
测试结果如下,符合预期



figure 2.jpeg

参考文献


  • Java并发编程之美 翟陆续、薛宾田著
欢迎关注我的公众号(个人简介处有微信公众号名称),一起去寻找文明的痕迹~
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表