软件编程
位置:首页>> 软件编程>> java编程>> Java InheritableThreadLocal用法详细介绍

Java InheritableThreadLocal用法详细介绍

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

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

其他网址传送门

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

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com