ThreadLocal
ThreadLocal
概述
ThreadLocal类用来提供线程内部的内部变量。这种变量在多线程环境下访问(通过get和set方法访问)时能保证各个线程的变量相对独立于其他线程内的变量。ThreadLocal实例通常来说都是private static类型,用于关联线程和线程的上下文
作用:
提供线程内的局部变量,不同的线程之间不会相互干扰,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数和组件之间一些公共变量的复杂度
常用方法
几个ThreadLocal常用的方法
方法声明 | 描述 |
---|---|
ThreadLocal() | 创建ThreadLocal对象 |
public void set(T value) | 设置当前线程绑定的局部变量 |
public T get() | 获取当前线程绑定的局部变量 |
public void remove() | 移除当前线程绑定的局部变量 |
ThreadLocal和Synchronized的区别
都能处理多线程并发访问变量的问题,但是处理角度和思路不同
Synchronized | ThreadLocal | |
---|---|---|
原理 | 同步机制采用“以时间换空间的方式”,只提供了一份变量,让不同的线程排队访问 | ThreadLocal采用“以空间换时间的”的方式,为每个线程提供了一份变量的副本,从而实现同时访问而像互不干扰 |
侧重点 | 多线程之间访问资源的同步 | 多线程中让每个线程之间的数据相互隔离 |
内部结构
常见误解
如果我们不去看源代码的话,可能会猜测ThreadLocal的设计是:每个ThreadLocal都创建一个Map,然后线程作为Map的key,存储在局部变量作为Map的value,这样就能达到各个线程的局部变量隔离的效果。这就是简单的设计方法,JDK最早期的ThreadLocal确实是这样设计的
现在的设计
在JDK8中ThreadLocal的设计是:每个Thread维护一个ThreadLocalMap,这个Map的key是ThreadLocal实例本身,value才是真正要存储的值Object
具体过程:
- 每个线程内部都要有一个Map(ThreadLocalMap)
- Map里面存储ThreadLoacal对象(key)和线程的变量副本(value)
- Thread内部的Map是由ThreadLocal维护的,由ThreadLocal负责向Map获取和设置的变量值
- 对于不同的线程,每次获取副本值时,别的线程并不能获取当前线程的副本值,形成副本隔离,互不干扰
核心源码
set方法
1 | public void set(T value) { |
get方法
1 | public T get() { |
remove方法
1 | public void remove() { |
源码分析
ThreadLocal的操作实际上是围绕着ThreadLocalMap展开的
基本结构
ThreadLocalMap是ThreadLocal的内部类,没有实现Map接口,独立的方式实现了Map的功能,其内部的Entry也是独立实现的
1 | ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { |
在ThreadLocalMap中,也是用Entry来保存K-V结构数据的。不过Entry中的key只能是ThreadLocal对象,这点构造方法中已经限定死了
另外,Entry继承WeakReference,也就是key(ThreadLocal)是弱引用,其目的是将ThreadLocal对象的生命周期和线程周期绑定
弱引用与内存泄露
有些人在使用ThreadLocal的过程中会发现有内存泄漏的情况发生,就猜测这个内存泄漏跟Entry中使用的弱引用的key有关系,这种理解是错误的