ThreadLocal的原理
ThreadLocal是一个支持泛型的java类,抛开里面的静态内部类ThreadLocalMap不说,其实它没几行代码,不信,您自己去看看。它用来干啥?类上注释说的很明白:
它能让线程拥有了自己内部独享的变量
每一个线程可以通过get、set方法去进行操作
可以覆盖initialValue方法指定线程独享的值
通常会用来修饰类里private static final的属性,为线程设置一些状态信息,例如user ID或者Transaction ID
每一个线程都有一个指向threadLocal实例的弱引用,只要线程一直存活或者该threadLocal实例能被访问到,都不会被垃圾回收清理掉。
话不多说,我们来看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();
}
获取当前线程内部的ThreadLocalMap
map存在则获取当前ThreadLocal对应的value值
map不存在或者找不到value值,则调用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;
}
调用initialValue方法,获取初始化值【调用者通过覆盖该方法,设置自己的初始化值】
获取当前线程内部的ThreadLocalMap
map存在则把当前ThreadLocal和value添加到map中
map不存在则创建一个ThreadLocalMap,保存到当前线程内部
小结
每一个线程都有一个私有变量,是ThreadLocalMap类型。当为线程添加ThreadLocal对象时,就是保存到这个map中,所以线程与线程间不会互相干扰。总结起来,一句话:我有我的map。
神奇的remove
因为ThreadLocal使用不当,会引发内存泄露的问题
示例一:
public class MemoryLeak { public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
TestClass t = new TestClass(i);
t.printId();
t = null;
}
}
}).start();
} static class TestClass{
private int id;
private int[] arr;
private ThreadLocal<TestClass> threadLocal;
TestClass(int id){
this.id = id;
arr = new int[1000000];
threadLocal = new ThreadLocal<>();
threadLocal.set(this);
} public void printId(){
System.out.println(threadLocal.get().id);
}
}
}
运行结果:
0
1
2
3
5...省略...
440
441
442
443
444
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
at com.gentlemanqc.MemoryLeak$TestClass.<init>(MemoryLeak.java:33)
at com.gentlemanqc.MemoryLeak$1.run(MemoryLeak.java:16)
at java.lang.Thread.run(Thread.java:745)
对上述代码稍作修改,请看:
public class MemoryLeak { public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
TestClass t = new TestClass(i);
t.printId();
t.threadLocal.remove();
}
}
}).start();
} static class TestClass{
private int id;
private int[] arr;
private ThreadLocal<TestClass> threadLocal;
TestClass(int id){
this.id = id;
arr = new int[1000000];
threadLocal = new ThreadLocal<>();
threadLocal.set(this);
} public void printId(){
System.out.println(threadLocal.get().id);
}
}
}
运行结果:
0
1
2
3
...省略...
996
997
998
999
一个内存泄漏,一个正常完成,对比代码只有一处不同:t = null改为了t.threadLocal.remove();哇,神奇的remove!!!
set(T value)源码
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
map存在则把当前ThreadLocal和value添加到map中
map不存在则创建一个ThreadLocalMap,保存到当前线程内部
remove源码
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
就一句话,获取当前线程内部的ThreadLocalMap,存在则从map中删除这个ThreadLocal对象。
无处不在的ThreadLocalMap
ThreadLocalMap是一个自定义的hash map,专门用来保存线程的thread local变量
它的操作仅限于ThreadLocal类中,不对外暴露
这个类被用在Thread类的私有变量threadLocals和inheritableThreadLocals上
为了能够保存大量且存活时间较长的threadLocal实例,hash table entries采用了WeakReferences作为key的类型
一旦hash table运行空间不足时,key为null的entry就会被清理掉
当创建一个ThreadLocalMap时,实际上内部是构建了一个Entry类型的数组,初始化大小为16,阈值threshold为数组长度的2/3,Entry类型为WeakReference,有一个弱引用指向ThreadLocal对象。
为什么Entry采用WeakReference类型?
Java垃圾回收时,看一个对象需不需要回收,就是看这个对象是否可达。什么是可达,就是能不能通过引用去访问到这个对象。(当然,垃圾回收的策略远比这个复杂,这里为了便于理解,简单给大家说一下)。
jdk1.2以后,引用就被分为四种类型:强引用、弱引用、软引用和虚引用。强引用就是我们常用的Object obj = new Object(),obj就是一个强引用,指向了对象内存空间。当内存空间不足时,Java垃圾回收程序发现对象有一个强引用,宁愿抛出OutofMemory错误,也不会去回收一个强引用的内存空间。而弱引用,即WeakReference,意思就是当一个对象只有弱引用指向它时,垃圾回收器不管当前内存是否足够,都会进行回收。反过来说,这个对象是否要被垃圾回收掉,取决于是否有强引用指向。ThreadLocalMap这么做,是不想因为自己存储了ThreadLocal对象,而影响到它的垃圾回收,而是把这个主动权完全交给了调用方,一旦调用方不想使用,设置ThreadLocal对象为null,内存就可以被回收掉。
注意:在使用完之后,最好手动调用threadlocal的remove()方法清理掉应用,防止内存泄漏。
ThreadLocal的原理的更多相关文章
- 线程局部变量ThreadLocal的原理及使用范围_1
线程局部变量ThreadLocal的原理及使用范围 使用原理 每个Thread中都有一个ThreadLocalMap成员, 该成员是ThreadLocal的内部类ThreadLocalMap类型.每使 ...
- ThreadLocal的原理和在框架中的应用
ThreadLocal的原理和在框架中的应用 博客分类: java基础 框架多线程SpringthreadDAO 概述 我们知道Spring通过各种DAO模板类降低了开发者使用各种数据持久 ...
- ThreadLocal的原理及产生的问题
点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人. 文章不定期同步公众号,还有各种一线大厂面试原题.我的学习系列笔记. ThreadLocal的原理 特点 ThreadLocal和Sychro ...
- ThreadLocal 工作原理、部分源码分析
1.大概去哪里看 ThreadLocal 其根本实现方法,是在Thread里面,有一个ThreadLocal.ThreadLocalMap属性 ThreadLocal.ThreadLocalMap t ...
- ThreadLocal工作原理
原文出处: imzoer 在这篇文章中,总结了一下面试过程中遇到的关于ThreadLocal的内容.总体上说,这样回答,面试算是过得去了.但是,这样的回答,明显仅仅是背会了答案,而没有去研究Threa ...
- ThreadLocal的原理,源码深度分析及使用
文章简介 ThreadLocal应该都比较熟悉,这篇文章会基于ThreadLocal的应用以及实现原理做一个全面的分析 内容导航 什么是ThreadLocal ThreadLocal的使用 分析Thr ...
- 对ThreadLocal实现原理的一点思考
前言 在<透彻理解Spring事务设计思想之手写实现>中,已经向大家揭示了Spring就是利用ThreadLocal来实现一个线程中的Connection是同一个,从而保证了事务.本篇博客 ...
- 【原理】Java的ThreadLocal实现原理浅读
当前线程的值传递,ThreadLocal 通过ThreadLocal设值,在线程内可获取,即时获取值时在其它Class或其它Method. public class BasicUsage { priv ...
- ThreadLocal实现原理
一.ThreadLocal介绍 这是一个线程的局部变量.也就是说,只有当前线程可以访问.既然是只有当前线程可以访问的数据,自然是线程安全的. 为每一个线程分配不同的对象,需要在应用 ...
- ThreadLocal使用原理、注意问题、使用场景
想必很多朋友对ThreadLocal并不陌生,今天我们就来一起探讨下ThreadLocal的使用方法和实现原理.首先,本文先谈一下对ThreadLocal的理解,然后根据ThreadLocal类的源码 ...
随机推荐
- basic deepwalk
Get to know How deepwalk works by this project. Two steps: 1. gen the graph, and gen the corpus on t ...
- Jenkins slave-agent.jnlp 无法安装为服务(install as a service)
问题: 在部署持续集成环境,配置slave节点时遇到一个问题,slave-agent.jnlp可以正常启动运行(不能正常启动的点这里) 但是在保存为系统服务时,slave-agent.jnlp点击[i ...
- 前端通过axios和FormData实现文件上传功能遇到的坑
使用element-ui中的upload上传组件,前端上传数据参数已经传过去了,但是后端 (java) 接不到数据 (null) [解决方案] html部分: <el-button type=& ...
- [洛谷P3943]:星空(DP+最短路)
题目传送门 题目背景 命运偷走如果只留下结果, 时间偷走初衷只留下了苦衷.你来过,然后你走后,只留下星空. 题目描述 逃不掉的那一天还是来了,小$F$看着夜空发呆.天上空荡荡的,没有一颗星星——大概是 ...
- docker 配置sonatype/nexus3
docker search nexusdocker pull sonatype/nexus3mkdir -p /dockermaven/nexus-datachmod -R 777 /dockerma ...
- HTML--JS 获取选择框信息
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 05 | 基础篇:某个应用的CPU使用率居然达到100%,我该怎么办?
通过前两节对平均负载和 CPU 上下文切换的学习,我相信你对 CPU 的性能已经有了初步了解.不过我还是想问一下,在学这个专栏前,你最常用什么指标来描述系统的 CPU 性能呢?我想你的答案,可能不是平 ...
- java调用数据库中的函数和存储过程
1.调用函数 {?= call <procedure-name>[(<arg1>,<arg2>, ...)]} {call <procedure-name&g ...
- Java_1.Java符号体系
Java符号包含五类:标识符.关键字.常量及字面量.运算符.分隔符 1.标识符 定义:用于标明程序中元素的名字,如类.方法和变量 命名规则: ·由字母.数字.下划线(_)和美元符号($)构成的字母序列 ...
- 20、numpy——IO
NumPy IO Numpy 可以读写磁盘上的文本数据或二进制数据. NumPy 为 ndarray 对象引入了一个简单的文件格式:npy. npy 文件用于存储重建 ndarray 所需的数据.图形 ...