ThreadLocal原理解析附源码

ThreadLocal可以用来存储线程局部的变量,ThreadLocal创建的变量只能被当前线程访问,其他线程无法修改。线程之间读写互相隔离。

一、原理解析

1、ThreadLocal类中存在一个ThreadLocalMap子类,实现与HashMap类似,ThreadLocalMap的键是ThreadLocal的一个弱引用。

弱引用:当该ThreadLocal对象的所有强引用都没有之后,弱引用会在下次GC时会被直接回收。

子类ThreadLocalMap的实现如下图。

2、看ThreadLocal类的set方法的源码,源码如下图所示。

1)先获取当前线程。

2)获取当前线程的ThreadLocal.ThreadLocalMap threadLocals变量,该变量为Thread类的成员变量。

3)如果线程的ThreadLocalMap变量不为空,直接赋值。

4)如果线程的ThreadLocalMap变量为空,调用new ThreadLocalMap(this, firstValue)来进行初始化ThreadLocalMap对象,并把当前对应ThreadLocal实例通过构造方法传入。

3、看ThreadLocal类的get方法的源码,逻辑比较简单,不再附源码。

1)获取当前线程。

2)获取当前线程对应的ThreadLocalMap变量。

3)通过ThreadLocalMap获取当前ThreadLocal实例对应的Entry,再获取对应的value。

4、当一个线程有多个ThreadLocal变量时,如何实现每个ThreadLocal不受影响,不会出现相互覆盖的情况。

private final int threadLocalHashCode = nextHashCode();

private static AtomicInteger nextHashCode = new AtomicInteger();

private static final int HASH_INCREMENT = 0x61c88647;

private static int nextHashCode() {
     return nextHashCode.getAndAdd(HASH_INCREMENT);
}

可以看到,ThreadLocal内部维护了一个静态常量hashcode值,每个ThreadLocal都唯一对应着一个hashcode值,确保在map的set/get操作时,不会出现覆盖的情况。

二、ThreadLocal使用的注意事项

通过源码明白了ThreadLocal的源码实现,但不能只是停留在表面,需要去深究源码为什么这么实现,使用起来有哪些需要注意的地方。

在日常开发中,需要以下几个问题。

1、ThreadLocalMap为什么维持着ThreadLocal的弱引用?内存泄漏的根因是什么?

引用关系见下图:

需要说明一个前提:若一个线程的生命周期很长。

首先,如果维持着强引用,肯定是不合理,会造成内存泄漏。维护着弱引用,当ThreadLocal被GC回收之后,ThreadLocalMap中的键也会随风而去,会出现很多key为null 值不为null的情况,会造成一定的内存泄漏。

直到线程消亡之后,线程的ThreadLocalMap才会被最终回收。

JVM中对此作了优化,调用set、get、remove方法时,会对键为null的map进行清理。

内存泄漏的根本原因是线程的生命周期很长,因为如果线程很快消亡,线程会对ThreadLocalMap进行清理的,不会出现内存泄漏。

2、线程池策略下的使用事项。

线程池策略执行下,某些线程会重复执行任务。试想一下,当run方法执行结束之前,不对ThreadLocal进行清理,会造成ThreadLocal的值出现覆盖的情况,会造成业务异常。

所以,在线程池的策略下使用ThreadLocal,需要像lock进行加锁和解锁一样,finally里面对ThreadLocal进行remove。

3、子线程如何使用父线程的ThreadLocal变量?

可以使用InheritableThreadLocal。

通过看Thread的源码发现:

/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

InheritableThreadLocal<T> extends ThreadLocal<T>

InheritableThreadLocal继承了ThreadLocal类,重写了getMap和createMap。操作的是线程的inheritableThreadLocals 变量。

再通过查看Thread的构造方法,可以发现,会对父线程的ThreadLocal变量进行复制,并将值赋给inheritableThreadLocals

如有不对之处,欢迎指正!

猜你喜欢

转载自www.cnblogs.com/zengleisure/p/11283842.html