1.ThreadLocal概念
ThreadLocal类用来提供线程内部的局部变量,可以理解为,线程本地变量副本。用于关联线程和线程上下文。
特性:
1.线程安全: 在多线程并发的场景下保证线程安全
2.传递数据: 我们可以通过ThreadLocal在同一线程,不同组件中传递公共变量
3.线程隔离: 每个线程的变量都是独立的,不会互相影响
2.常用方法
方法声明 | 描述 |
ThreadLocal() | 创建ThreadLocal对象 |
public void set( T value) | 设置当前线程绑定的局部变量 |
public T get() | 获取当前线程绑定的局部变量 |
public void remove() | 移除当前线程绑定的局部变量 |
3. ThreadLocal与synchronized的区别
synchronized | ThreadLocal | |
原理 | 同步机制采用'以时间换空间'的方式, 只提供了一份变量,让不同的线程排队访问 | ThreadLocal采用'以空间换时间'的方式, 为每一个线程都提供了一份变量的副本,从而实现同时访问而相不干扰 |
侧重点 | 多个线程之间访问资源的同步 | 多线程中让每个线程之间的数据相互隔离 |
4 ThreadLocal的优势
1.传递数据 : 保存每个线程绑定的数据,在需要的地方可以直接获取, 避免参数直接传递带来的代码耦合问题
2.线程隔离 : 各线程之间的数据相互隔离却又具备并发性,避免同步方式带来的性能损失
5. ThreadLocal的内部结构
在JDK8中 ThreadLocal的设计是:每个Thread维护一个ThreadLocalMap,这个Map的key是ThreadLocal实例本身,value才是真正要存储的值Object。
ThreadLocalMap是ThreadLocal的内部类。
具体的过程是这样的:
1.每个Thread线程内部都有一个Map (ThreadLocalMap)
2.Map里面存储ThreadLocal对象(key)和线程的变量副本(value)
3.Thread内部的Map是由ThreadLocal维护的,由ThreadLocal负责向map获取和设置线程的变量值。
4.对于不同的线程,每次获取副本值时,别的线程并不能获取到当前线程的副本值,形成了副本的隔离,互不干扰。
6.内存泄漏相关概念
Memory overflow:内存溢出,没有足够的内存提供申请者使用。
Memory leak: 内存泄漏是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。内存泄漏的堆积终将导致内存溢出。
7.强引用 - 弱引用
强引用(“Strong” Reference),就是我们最常见的普通对象引用,只要还有强引用指向一个对象,就能表明对象还“活着”,垃圾回收器就不会回收这种对象。
弱引用(WeakReference),垃圾回收器一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
8.为什么ThreadLocal出现内存泄漏与ThreadLocalMap中的key使用哪种引用类型无关
使用强引用,垃圾回收期不会回收这种对象。
使用弱引用,如果当前线程仍然在运行,但是ThreadLocalMap的key被垃圾回收了,value永远不会被访问到了,导致value内存泄漏。
9.ThreadLocal出现内存泄漏的真实原因
1. 没有手动删除这个Entry
2. CurrentThread依然运行
10.ThreadLocalMap中的key为什么使用弱引用
无论ThreadLocalMap中的key使用哪种类型引用都无法完全避免内存泄漏,跟使用弱引用没有关系。
要避免内存泄漏有两种方式:
1.使用完ThreadLocal,调用其remove方法删除对应的Entry
2.使用完ThreadLocal,当前Thread也随之运行结束
相对第一种方式,第二种方式显然更不好控制,特别是使用线程池的时候,线程结束是不会销毁的。
也就是说,只要记得在使用完ThreadLocal及时的调用remove,无论key是强引用还是弱引用都不会有问题。那么为什么key要用弱引用呢?
事实上,在ThreadLocalMap中的set/getEntry方法中,会对key为null(也即是ThreadLocal为null)进行判断,如果为null的话,那么是会对value置为null的。
这就意味着使用完ThreadLocal,CurrentThread依然运行的前提下,就算忘记调用remove方法,弱引用比强引用可以多一层保障:弱引用的ThreadLocal会被回收,对应的value在下一次ThreadLocalMap调用set,get,remove中的任一方法的时候会被清除,从而避免内存泄漏。