Java InheritableThreadLocal用法详细介绍

作者:IT利刃出鞘 时间:2023-01-27 04:30:11 

简介

本文介绍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,只是在创建子线程的&ldquo;一刹那&rdquo;将父线程的ThreadLocalMap复制给子线程,后续两者就没啥关系了。

其他网址传送门

来源:https://knife.blog.csdn.net/article/details/126644228

标签:Java,InheritableThreadLocal
0
投稿

猜你喜欢

  • C语言回溯法解八皇后问题(八皇后算法)

    2021-11-12 03:27:15
  • 浅谈Android手机的抢红包插件

    2021-08-13 01:04:36
  • Springboot JPA 枚举Enum类型存入到数据库的操作

    2023-11-25 16:11:40
  • springboot使用事物注解方式代码实例

    2022-07-09 00:13:21
  • 基于JavaMail API收发邮件的方法

    2022-03-10 09:34:24
  • C#基础知识之this关键字介绍

    2022-02-08 10:59:16
  • ChatGPT介绍及Java API调用

    2021-07-06 07:53:55
  • Android应用程序转到后台并回到前台判断方法

    2022-11-12 19:49:35
  • MyBatis之自查询使用递归实现 N级联动效果(两种实现方式)

    2023-04-15 03:34:50
  • 一文告诉你为什么要重写hashCode()方法和equals()方法

    2021-09-05 07:05:55
  • java编程题之顺时针打印矩阵

    2022-02-22 04:05:36
  • spring security获取用户信息的实现代码

    2022-09-29 01:59:03
  • springboot html调用js无效400问题及解决

    2023-06-24 02:11:54
  • Android性能优化以及数据优化方法

    2021-09-02 03:46:18
  • android通过自定义toast实现悬浮通知效果的示例代码

    2022-08-11 03:23:54
  • HashMap红黑树入门(实现一个简单的红黑树)

    2022-10-18 13:52:35
  • java与微信小程序实现websocket长连接

    2021-09-10 11:32:11
  • Android实现简单卡片布局

    2023-05-22 11:43:37
  • java8中的lambda表达式简介

    2022-09-12 04:14:10
  • 详解C语言实现猜数字游戏

    2023-11-03 04:58:51
  • asp之家 软件编程 m.aspxhome.com