Java InheritableThreadLocal用法详细介绍

目录
  • 简介
  • 问题复现
  • 解决方案
  • 源码分析
  • 注意

简介

本文介绍InheritableThreadLocal的用法。

ThreadLocal可以将数据绑定当前线程,如果希望当前线程的ThreadLocal的数据被子线程使用,实现方式就会相当困难(需要用户自己在代码中传递)。

InheritableThreadLocal可以方便地让子线程自动获取父线程ThreadLocal的数据。

ThreadLocal和InheritableThreadLocal都要注意,用完后要调用其remove()方法,不然可能导致内存泄露或者产生脏数据。

问题复现

代码

package com.example.a;
public class Demo {
    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
    public static void main(String[] args) {
        threadLocal.set("hello");
        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);
                // 一定要remove,不然可能导致内存泄漏
                threadLocal.remove();
            }
        });
        thread.start();
    }
}

结果(子线程无法获取父线程设置的值)

主线程获取的value:hello
子线程获取的value:null

解决方案

只需要将ThreadLocal变成InheritableThreadLocal。

代码

package com.example.a;
public class Demo {
    private static InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();
    public static void main(String[] args) {
        inheritableThreadLocal.set("hello");
        System.out.println("主线程获取的value:" + inheritableThreadLocal.get());
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                String value = inheritableThreadLocal.get();
                System.out.println("子线程获取的value:" + value);
                // 一定要remove,不然可能导致内存泄漏
                inheritableThreadLocal.remove();
            }
        });
        thread.start();
    }
}

结果(子线程可以获取父线程设置的值)

主线程获取的value:hello
子线程获取的value:hello

源码分析

源码查看

InheritableThreadLocal的源代码:

package java.lang;
import java.lang.ref.*;
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方法,区别是:InheritableThreadLocal将 ThreadLocal 中的 threadLocals 换成了 inheritableThreadLocals,这两个变量都是ThreadLocalMap类型,并且都是Thread类的属性。

InheritableThreadLocal为什么能拿到父线程中的ThreadLocal值?

1.InheritableThreadLocal的get方法

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中。

2.子线程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那样来使用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

  • Java InheritableThreadLocal用法详细介绍

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

  • Java多线程的用法详细介绍

    Java多线程的用法详细介绍 最全面的Java多线程用法解析,如果你对Java的多线程机制并没有深入的研究,那么本文可以帮助你更透彻地理解Java多线程的原理以及使用方法. 1.创建线程 在Java中创建线程有两种方法:使用Thread类和使用Runnable接口.在使用Runnable接口时需要建立一个Thread实例.因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或它的子类的实例.Thread构造函数: public Thread( ); publi

  • Spring中@Transactional用法详细介绍

    Spring中@Transactional用法详细介绍 引言: 在spring中@Transactional提供一种控制事务管理的快捷手段,但是很多人都只是@Transactional简单使用,并未深入了解,其各个配置项的使用方法,本文将深入讲解各个配置项的使用. 1.  @Transactional的定义 Spring中的@Transactional基于动态代理的机制,提供了一种透明的事务管理机制,方便快捷解决在开发中碰到的问题.在现实中,实际的问题往往比我们预期的要复杂很多,这就要求对@Tr

  • nginx命令参数用法详细介绍

    nginx命令参数用法详细介绍 nginx命令:启动nginx 在Windows上安装好nginx后,我们需要启动nginx服务,启动nginx服务的命令行操作主要有两种方式,即 C:/nginx-0.8.53>nginx.exe 或者 C:/nginx-0.8.53>start nginx 启动nginx命令说明:需要注意,由于nginx默认端口也是80端口,如果此时你的机器上开启了Apache或者IIS服务,切忌在启动nginx之前务必关闭IIS或Apache服务,否则nginx启动命令不

  • PHP中error_reporting函数用法详细介绍

    PHP中error_reporting函数用法详细介绍 PHP中对错误的处理会用到error_reporting函数,看到最多的是error_reporting(E_ALL ^ E_NOTICE),这个是什么意思呢?下面我们具体分析error_reporting函数. 定义用法 error_reporting() 设置 PHP 的报错级别并返回当前级别. 语法 error_reporting(report_level) 如果参数 report_level 未指定,当前报错级别将被返回.下面几项是

  • Java操作Redis详细介绍

    1. 简介 Redis 是一个开源(BSD许可)的,内存中的key-value存储系统,它可以用作数据库.缓存和消息中间件. 2. 对key的操作 首先要建立连接Jedis jedis = new Jedis("127.0.0.1", 6379),然后就可以对string,set,zset,hash进行操作了. //对key的测试 public void keyTest() { System.out.println(jedis.flushDB()); //清空数据 System.out

  • Python torch.onnx.export用法详细介绍

    目录 函数原型 参数介绍 mode (torch.nn.Module, torch.jit.ScriptModule or torch.jit.ScriptFunction) args (tuple or torch.Tensor) f export_params (bool, default True) verbose (bool, default False) training (enum, default TrainingMode.EVAL) input_names (list of st

  • java枚举使用详细介绍及实现

    java枚举使用详解 在实际编程中,往往存在着这样的"数据集",它们的数值在程序中是稳定的,而且"数据集"中的元素是有限的. 例如星期一到星期日七个数据元素组成了一周的"数据集",春夏秋冬四个数据元素组成了四季的"数据集". 在java中如何更好的使用这些"数据集"呢?因此枚举便派上了用场,以下代码详细介绍了枚举的用法. package com.ljq.test; /** * 枚举用法详解 * * @aut

  • Java多线程ThreadAPI详细介绍

    1.Thread的构造方法 package threadAPI; public class CreateThread { public static void main(String[] args) { Thread t1 = new Thread(); Thread t2 = new Thread(); t1.start(); t2.start(); System.out.println(t1.getName()); System.out.println(t2.getName()); } }

  • Java enum的用法详细介绍及实例代码

    Java enum的用法 用法一:常量 在JDK1.5 之前,我们定义常量都是: public static fianl.... .现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法. public enum Color { RED, GREEN, BLANK, YELLOW } 用法二:switch JDK1.6之前的switch语句只支持int,char,enum类型,使用枚举,能让我们的代码可读性更强. enum Signal { GREEN, YEL

随机推荐