欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

ThreadLocal和InheritableThreadLocal源码解析

程序员文章站 2022-04-12 18:36:16
转载本文章请标明作者和出处本文出自《爱喝纯净水的南荣牧歌》加油,程序猿!!!ThreadLocal作用多线程访问同一个变量的时候会有线程安全的问题,ThreadLocal会把每一个线程访问的变量变成这个线程私有的变量,从而避免了线程安全的问题。使用代码ps: 其中线程休眠,是为了让其他线程有机会在赋值之后再操作ThreadLocal;public class ThreadLocalTest { public static void main(String[] args) {....

ThreadLocal

作用

多线程访问同一个变量的时候会有线程安全的问题,ThreadLocal会把每一个线程访问的变量变成这个线程私有的变量,从而避免了线程安全的问题。

使用代码

ps: 其中线程休眠,是为了让其他线程有机会在赋值之后再操作ThreadLocal;

public class ThreadLocalTest { public static void main(String[] args) { ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); for (int x = 1; x <= 5; x++) { final int index = x; Thread thread = new Thread(() -> { threadLocal.set(index); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " : " + threadLocal.get()); threadLocal.remove(); }); thread.start(); } } } 
  • 运行结果:
    ThreadLocal和InheritableThreadLocal源码解析
    可以验证,ThreadLocal保证了变量是被每一个线程肚子拥有的,从而避免了线程安全的问题。

原理

  • 下面是源码中ThreadLocal中的set方法
 public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } 

我们可以看到他用当前线程做key获取到了一个map,而这个map就是Thread线程类本身维护的一个变量ThreadLocal和InheritableThreadLocal源码解析

所以,其实,ThreadLocal的变量是存储在每个线程本身的threadLocals map变量中,这样也是很合理的,每个线程都有自己的容器来存储自己线程特有的变量,而这个map的key就是ThreadLocal的引用this,这样就可以保证,每个线程可以存储多个ThreadLocal的变量。

当然这个threadLocals不是在县城创建的时候初始化的,是在ThreadLocal第一次get或者set的时候检测为空的话就会进行初始化。

  • 下面是源码中ThreadLocal中的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(); } 
  • setInitialValue方法源码
 private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; } 

如果map不为空的话,那么就用ThreadLocal的引用this,去拿当前ThreadLocal存储的值,如果这时候线程中的threadLocals变量还没有初始化就要走setInitialValue方法。这个方法,会将threadLocals变量进行初始化,并且用ThreadLocal的引用this当key,null做值,进行一个初始化,并返回null。

  • remove方法
 public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); } 

如果使用了ThreadLocal向当前的线程里面set了值的话,如果线程不结束,这个值是不会消失的,如果你想删除这个值,就可以调用remove方法,其实就是把这个Entry从map中删除而已。

ThreadLocal的问题

不支持继承性

public class ThreadLocalTest { public static void main(String[] args) { ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); threadLocal.set(111); new Thread(() -> System.out.println(threadLocal.get())).start(); } } 

上面这理所当然的取不出来值,打印结果为null,因为子线程的threadLocals并没有存主线程的值,而要想解决这个问题,就需要认识一个新的工具InheritableThreadLocal。

InheritableThreadLocal

上文说了,InheritableThreadLocal是为了解决ThreadLocal不支持继承性而产生的,那我们先看一下使用了InheritableThreadLocal的效果。

public class ThreadLocalTest { public static void main(String[] args) { InheritableThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>(); threadLocal.set(111); new Thread(() -> System.out.println(threadLocal.get())).start(); } } 
  • 运行结果
    ThreadLocal和InheritableThreadLocal源码解析
    果不其然,我们获取到了主线程set到的111。

原理解析

打开InheritableThreadLocal类的源码,我们可以看到他是继承了ThreadLocal类,并且仅仅重写了三个方法
ThreadLocal和InheritableThreadLocal源码解析
其中inheritableThreadLocals肯定引起了你的注意,这个变量和threadLocals变量一样,也是每一个线程私有的一个map,name这个的作用是什么的?为什么InheritableThreadLocals将返回的map变成了InheritableThreadLocals就可以实现继承性了呢,我们看一下Thread类的源码。我们在线程的初始化方法init中看到了
ThreadLocal和InheritableThreadLocal源码解析
如果父线程的InheritableThreadLocals不为空,就会复制给子线程,这样,父线程的变量当然就可以被子线程看到啦,因为这里进行了同步,所以inheritableThreadLocals可以实现线程的继承性

本文地址:https://blog.csdn.net/qq_36929361/article/details/108245175

相关标签: 多线程 java