-
Notifications
You must be signed in to change notification settings - Fork 120
ThreadLoacal的实现原理
ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,且存储的数据只有在该线程中才能获取的到,其他线程无法获取。
public static void main(String[] args) throws InterruptedException {
ThreadLocal<Boolean> threadLocal=new ThreadLocal<>();
new Thread(){
@Override
public void run() {
super.run();
threadLocal.set(true);
System.out.println("在子线程中获得threadLocal中的值:"+threadLocal.get());
}
}.start();
Thread.sleep(100);
System.out.println("在主线程中获得threadLocal中的值:"+threadLocal.get());
}
输出结果:
在子线程中获得threadLocal中的值:true
在主线程中获得threadLocal中的值:null
ThreadLocal是一个泛型类。它的重点是有一个ThreadLocalMap的静态内部类和两个核心方法,get和set。
ThreadLocalMap是一个存储(key,value)键值对的类,它的实现与HashMap有些类似,这里不再详细赘述。它比较特别的地方是它内部的节点Entry:
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
Entry是位于ThreadLocalMap中的静态内部类,表示Map的节点(key,value),并且它继承了WeakReference,WeakReference泛型为ThreadLocal。看Entry的构造方法有两个参数ThreadLocal与一个Object,分别表示key和value。这里注意,在构造方法中将ThreadLocal作为参数调用了父类的构造方法,也就是ThreadLocalMap的key是一个弱引用,而value是一个强引用。意味着,当没有引用指向key时,key会在下一次垃圾回收时被JVM回收。
那ThreadLocalMap在哪里使用了呢?其实就在线程Thread类中:
class Thread implements Runnable {
ThreadLocal.ThreadLocalMap threadLocals = null;
}
可以看到Thread类中维护了一个ThreadLocalMap的成员变量。
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);
}
}
// 获取当前线程的ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
// 为线程t创建ThreadLocalMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
set方法的源码很简单,就是以当前的ThreadLocal作为key将value放到ThreadLocalMap中,ThreadLocalMap是位于ThreadLocal中的一个静态内部类。
这么做其实是为了有利于垃圾回收。想一下如果没有将ThreadLocal的key声明为弱引用会有什么问题?当使用完了ThreaLocal之后,解除了对它的引用,希望它能够被回收,但是此时JVM发现ThreadLocal无法被回收,因为在ThreadLocalMap中,ThreadLocal还作为key被引用,虽然ThreadLocal已经不再使用,但是仍然无法被回收。而如果将ThreadLocalMap的key声明为弱引用就不会存在无法回收ThreadLocal的问题。
get方法就是从ThreadLocal中取值,它的源码如下:
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方法的源码也不难,就是拿到当前线程中的ThreadLocalMap,然后将当前ThreadLocal作为key查找set进去的值。
了解了ThreadLocal的内部实现后,我们通过开篇的例题来分析以下ThreadLoca的存储过程:
-
(1)threadLoca在子线程(称为threadA)中通过threadLocal的set方法将值设置为了true。在set方法中会首先获得threadA线程,然后拿到threadA中的ThreadLocalMap,并将threadLocal作为key,将true作为value存储到了ThreadLocalMap中;
-
(2)当在threadA线程中调用threadLocal的get方法时,get方法同样会先获取到threadA,然后拿到threadA中的ThreadLocalMap,接着以threadLocal作为key来获取ThreadLocalMap中对应的值,此时的ThreadLocalMap与第(1)不中的ThreadLocalMap是同一个,且key都是threadLocal。因此,在threadLocal中拿到的值为true;
-
(3)在主线程中调用threadLocal的get操作,此时get方法会首先获取到当前线程为主线程,然后拿到了主线程中的ThreadLocalMap,接着以threadLocal作为key来获取主线程的ThreadLocalMap中对应的值,此时的ThreadLocalMap与(1)步中的ThreadLocalMap并非同一个。因此,主线程中获取到的值是null。
ThreadLocal的示意图如下:
- JMM与volatile关键字
- synchronized的实现原理
- synchronized等待与唤醒机制
- AQS的实现原理
- ReentrantLock的实现原理
- ReentrantLock等待与唤醒机制
- CAS、Unsafe类以及Automic并发包
- ThreadLocal的实现原理
- 线程池的实现原理
- Java线程中断机制
- 多线程与并发常见面试题
- Android基础知识汇总
- MVC、MVP与MVVM
- SparseArray实现原理
- ArrayMap的实现原理
- SharedPreferences
- Bitmap
- Activity的启动模式
- Fragment核心原理
- 组件化项目架构搭建
- 组件化WebView架构搭建
- 为什么 Activity.finish() 之后 10s 才 onDestroy ?
- Binder与AIDL
- Binder实现原理
- Android系统启动流程
- InputManagerService
- WindowManagerService
- Choreographer详解
- SurfaceFlinger
- ViewRootImpl
- ActivityManagerService
- APP启动流程
- PMS安装与签名校验
- Dalvik与ART
- 内存优化策略
- UI界面及卡顿优化
- App启动优化
- ANR问题
- 包体积优化
- APK打包流程
- 电池电量优化
- Android屏幕适配
- 线上性能监控1--线上监控切入点
- 线上性能监控2--Matrix实现原理
- Glide实现原理
- OkHttp实现原理
- Retrofit实现原理
- RxJava实现原理
- RxJava中的线程切换与线程池
- LeakCanary实现原理
- ButterKnife实现原理
- ARouter实现原理
- Tinker实现原理
- 2. 两数相加
- 19.删除链表的倒数第 N 个结点
- 21. 合并两个有序链表
- 24. 两两交换链表中的节点
- 61. 旋转链表
- 86. 分隔链表
- 92. 反转链表 II
- 141. 环形链表
- 160. 相交链表
- 206. 反转链表
- 206 反转链表 扩展
- 234. 回文链表
- 237. 删除链表中的节点
- 445. 两数相加 II
- 面试题 02.02. 返回倒数第 k 个节点
- 面试题 02.08. 环路检测
- 剑指 Offer 06. 从尾到头打印链表
- 剑指 Offer 18. 删除链表的节点
- 剑指 Offer 22. 链表中倒数第k个节点
- 剑指 Offer 35. 复杂链表的复制
- 1. 两数之和
- 11. 盛最多水的容器
- 53. 最大子序和
- 75. 颜色分类
- 124.验证回文串
- 167. 两数之和 II - 输入有序数组 -169. 多数元素
- 189.旋转数组
- 209. 长度最小的子数组
- 283.移动0
- 303.区域和检索 - 数组不可变
- 338. 比特位计数
- 448. 找到所有数组中消失的数字
- 643.有序数组的平方
- 977. 有序数组的平方