Java InheritableThreadLocal使用示例详解

目录
  • 引子
  • InheritableThreadLocal简单使用
  • InheritableThreadLocal原理分析

引子

public class InheritableThreadLocalDemo {
    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
    public static void main(String[] args) {
        threadLocal.set("mainThread");
        System.out.println("value:"+threadLocal.get());
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                String value = threadLocal.get();
                System.out.println("value:"+value);
            }
        });
        thread.start();
    }
}

上面代码中在主线程中设置了一个ThreadLocal变量,并将其值设置为mainThread。然后有在主线程中开启了一个子线程thread,并试图获取在主线程中set的ThreadLocal变量的值。但是结果如下:

value:mainThread
value:null

通过前面的文章介绍,对于上面的结果我们也就非常容易理解了。每个线程都会有一个自己的ThreadLocalMap,所以子线程在调用get方法拿值的时候其实访问的是自己的ThreadLocalMap,这个Map和主线程的Map是两个不同的对象,所以肯定是拿不到值的。

那么Java中有没有类似的对象能实现上面的功能呢?有,InheritableThreadLocal就能实现这样的功能,这个类能让子线程继承父线程中已经设置的ThreadLocal值。

InheritableThreadLocal简单使用

还是以上面的列子为列,我们只需要将ThreadLocal变成InheritableThreadLocal就行了。

public class InheritableThreadLocalDemo {
    private static InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
    public static void main(String[] args) {
        threadLocal.set("mainThread");
        System.out.println("value:"+threadLocal.get());
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                String value = threadLocal.get();
                System.out.println("value:"+value);
            }
        });
        thread.start();
    }
}

执行结果如下:

value:mainThread
value:mainThread

InheritableThreadLocal原理分析

先看下InheritableThreadLocal的源代码:

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

这个类继承了ThreadLocal,并且重写了getMap和createMap方法,区别就是将 ThreadLocal 中的 threadLocals 换成了 inheritableThreadLocals,这两个变量都是ThreadLocalMap类型,并且都是Thread类的属性。

下面就一步步来看下InheritableThreadLocal为什么能拿到父线程中的ThreadLocal值。

step1:InheritableThreadLocal获取值先调用了get方法,所以我们直接看看get方法都做了些啥。

  public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

从上面的代码可以看出,get方法和ThreadLocal中是一样的,唯一有区别的就是其中的getMap方法重写了,返回的是inheritableThreadLocals属性。这个属性也是一个ThreadLocalMap类型的变量。那么从这边就可以推断出来:肯定是在某处将父线程中的ThreadLocal值赋值到了子线程的inheritableThreadLocals中。

step2:在源代码中搜索哪些地方使用到了inheritableThreadLocals这个属性,最后找到这段代码:

private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }
        this.name = name.toCharArray();
        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            if (security != null) {
                g = security.getThreadGroup();
            }
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }
        g.checkAccess();
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }
        g.addUnstarted();
        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
        //1. 这边先判断了父线程中inheritableThreadLocals属性是否为空,不为空的话就复制给子线程
        if (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();
    }

上面的代码印证了我们的猜想。需要注意的是一旦子线程被创建以后,再操作父线程中的ThreadLocal变量,那么子线程是不能感知的。因为父线程和子线程还是拥有各自的ThreadLocalMap,只是在创建子线程的“一刹那”将父线程的ThreadLocalMap复制给子线程,后续两者就没啥关系了。

到此这篇关于Java InheritableThreadLocal使用示例详解的文章就介绍到这了,更多相关Java InheritableThreadLocal内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java InheritableThreadLocal用法详细介绍

    目录 简介 问题复现 解决方案 源码分析 注意 简介 本文介绍InheritableThreadLocal的用法. ThreadLocal可以将数据绑定当前线程,如果希望当前线程的ThreadLocal的数据被子线程使用,实现方式就会相当困难(需要用户自己在代码中传递). InheritableThreadLocal可以方便地让子线程自动获取父线程ThreadLocal的数据. ThreadLocal和InheritableThreadLocal都要注意,用完后要调用其remove()方法,不然

  • java多线程编程之InheritableThreadLocal

    InheritableThreadLocal的作用: 当我们需要在子线程中使用父线程中的值得时候我们就可以像使用ThreadLocal那样来使用InheritableThreadLocal了. 首先我们来看一下InheritableThreadLocal的jdk源码: package java.lang; import java.lang.ref.*; public class InheritableThreadLocal<T> extends ThreadLocal<T> { p

  • Java InheritableThreadLocal使用示例详解

    目录 引子 InheritableThreadLocal简单使用 InheritableThreadLocal原理分析 引子 public class InheritableThreadLocalDemo { private static ThreadLocal<String> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { threadLocal.set("mainT

  • JSON在Java中的相互转换示例详解

    什么是JSON? JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式. 易于人阅读和编写.同时也易于机器解析和生成. JSON采用完全独立于语言的文本格式,而且很多语言都提供了对json的支持(包括C, C++, C#, Java, JavaScript, Perl, Python等). 这样就使得JSON成为理想的数据交换格式. 数据交换格式,是指客户端和服务器之间通信,传递数据时,数据使用的格式是json JSON在java中的使用 json在ja

  • Java NIO通信基础示例详解

    目录 Java NIO 通信基础介绍 NIO 和 OIO 的对比 使用 FileChannel 完成文件复制的实践案例 使用 DatagramChannel 数据包通道发送数据的实践案例 使用 NIO 实现 Discard 服务器的实践案例 Java NIO 通信基础介绍 高性能的 Java 通信,绝对离不开 Java NIO 技术,现在主流的技术框架或中间件服务器,都使 用了 Java NIO 技术,譬如:Tomcat.Jetty.Netty. Java NIO 由以下三个核心组件组成: Ch

  • 教你正确的Java扩展方法示例详解

    目录 引言 支持扩展方法的语言 C# Visual Basic Kotlin 主角登场 Lombok @ExtensionMethod Manifold 总结 引言 扩展方法能够向现有类型“添加”方法,而无需创建新的派生类型.重新编译或以其他方式修改原始类型.扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样进行调用.对于用 C# 和 Visual Basic 编写的客户端代码,调用扩展方法与调用在类型中实际定义的方法没有明显的差异. 支持扩展方法的语言 其实比较多的编程语言都支持了

  • JAVA构造函数(方法)示例详解

    目录  一.什么是构造函数 二.构造函数的特点 三.示例 四.默认构造函数 五.构造函数的重载 六.构造函数的使用 构造函数的特点 总结  一.什么是构造函数 Java构造函数,也叫构造方法,是JAVA中一种特殊的函数.与函数名相同,无返回值. 作用:一般用来初始化成员属性和成员方法的,即new对象产生后,就调用了对象的属性和方法. 在现实生活中,很多事物一出现,就天生具有某些属性和行为.比如人一出生,就有年龄.身高.体重.就会哭:汽车一出产,就有颜色.有外观.可以运行等. 这些,我们就可以将这

  • java list用法示例详解

    |--List:元素是有序的(怎么存的就怎么取出来,顺序不会乱),元素可以重复(角标1上有个3,角标2上也可以有个3)因为该集合体系有索引,  |-- ArrayList:底层的数据结构使用的是数组结构(数组长度是可变的百分之五十延长)(特点是查询很快,但增删较慢)线程不同步  |-- LinkedList:底层的数据结构是链表结构(特点是查询较慢,增删较快)  |-- Vector:底层是数组数据结构 线程同步(数组长度是可变的百分之百延长)(无论查询还是增删都很慢,被ArrayList替代了

  • java反射机制示例详解

    1.什么是反射?一个类有多个组成部分,例如:成员变量,方法,构造方法等.反射就是加载类,并解剖出类的各个组成部分. 2.加载类java中有一个Class类用于代表某一个类的字节码.Class类既然代表某个类的字节码,那就要提供加载某个类字节码的方法:forName().   此方法用于加载某个类的字节码到内存中,并使用class对象进行封装.另外2种得到class对象的方式:类名.class对象.getClass() 先创建一个简单的Person类 复制代码 代码如下: public class

  • Java中的静态内部类详解及代码示例

    1. 什么是静态内部类 在Java中有静态代码块.静态变量.静态方法,当然也有静态类,但Java中的静态类只能是Java的内部类,也称为静态嵌套类.静态内部类的定义如下: public class OuterClass { static class StaticInnerClass { ... } } 在介绍静态内部类之前,首先要弄清楚静态内部类与Java其它内部类的区别. 2. 内部类 什么是内部类?将一个类的定义放在另一个类的内部,就是内部类.Java的内部类主要分为成员内部类.局部内部类.

  • Java中的HashSet详解和使用示例_动力节点Java学院整理

    第1部分 HashSet介绍 HashSet 简介 HashSet 是一个没有重复元素的集合. 它是由HashMap实现的,不保证元素的顺序,而且HashSet允许使用 null 元素. HashSet是非同步的.如果多个线程同时访问一个哈希 set,而其中至少一个线程修改了该 set,那么它必须 保持外部同步.这通常是通过对自然封装该 set 的对象执行同步操作来完成的.如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来"包装" set.

  • Java 中的HashMap详解和使用示例_动力节点Java学院整理

    第1部分 HashMap介绍 HashMap简介 HashMap 是一个散列表,它存储的内容是键值对(key-value)映射. HashMap 继承于AbstractMap,实现了Map.Cloneable.java.io.Serializable接口. HashMap 的实现不是同步的,这意味着它不是线程安全的.它的key.value都可以为null.此外,HashMap中的映射不是有序的. HashMap 的实例有两个参数影响其性能:"初始容量" 和 "加载因子&quo

随机推荐