ThreadLocal是什么
ThreadLocal提供线程局部变量。这些变量与正常的变量不同,因为每一个线程在访问ThreadLocal实例的时候(通过其get或set方法) 都有自己的、独立初始化的变量副本 。ThreadLocal实例通常是类中的私有静态字段,使用它的目的是希望将状态(例如,用户ID或事务ID)与线程关联起来。
简单来说:ThreadLocal类为每个Thread提供一个只属于自己的本地变量,不会被其他Thread所影响。
ThreadLocal的api
例子
public class Main {
public static void main(String[] args) {
test t = new test();
new Thread(()->{
for(int i=0;i<5;i++){
t.addNum();
}
System.out.println("t1 num:" + t.getNum());
},"t1").start();
new Thread(()->{
for(int i=0;i<8;i++){
t.addNum();
}
System.out.println("t2 num:" + t.getNum());
},"t2").start();
new Thread(()->{
for(int i=0;i<10;i++){
t.addNum();
}
System.out.println("t3 num:" + t.getNum());
},"t3").start();
}
}
class test {
ThreadLocal<Integer> integerThreadLocal = ThreadLocal.withInitial(() -> 0);
public test(){
//
}
public int getNum(){
return integerThreadLocal.get();
}
public void addNum(){
integerThreadLocal.set(integerThreadLocal.get() + 1);
}
}
// t1 num:5
// t2 num:8
// t3 num:10
api源码
在阿里手册中有一条使用SimpleDateFormat类的注意事项,SimpleDateFormat类中的Calendar类是线程不安全的。
ThreadLocal的数据结构
Thread和ThreadLocal
每一个Thread都有一份自己的ThreadLocal
ThreadLocal和ThreadLocalMap
可以简单理解为KV的数据结构
强、软、弱和虚引用
强引用
当内存不足,JVM开始垃圾回收,对于强引用的对象, 就算是出现了OOM也不会对该对象进行回收 , 死都不收。
软引用
软引用通常用在对内存敏感的程序中,比如高速缓存就有用到软引用, 内存够用的时候就保留,不够用就回收。
弱引用
对于只有弱引用的对象来说,只要垃圾回收机制一运行,不管JVM的内存空间是否足够,都会回收该对象占用的内存。
虚引用
虚引用的主要作用是跟踪对象被垃圾回收的状态。仅仅是提供了一种确保对象被finalize以后,做某些事情的机制。PhantomReference的get方法总是返回null,因此无法访问对应的引用对象。
其意义在于:说明一个对象已经进入finalization阶段,可以被gc回收,用来实现比finalization机制更灵活的回收操作 。
换句话说,设置虚引用关联的唯一目的,就是在这个对象被收集器回收的时候收到一个系统通知或者后续添加进一步的处理。
ThreadLocal内存泄露问题
什么是内存泄漏
不再会被使用的对象或者变量占用的内存不能被回收,就是内存泄漏。
ThreadLocal引用图
ThreadLocalMap可能会出现k被收了,但是v还存在导致OOM,解决方法需要手动回收(调用remove方法)。
ThreadLocal的总结
ThreadLocal并不是解决线程间共享数据的问题。
ThreadLocal适用于变量在线程间隔离且在方法间共享的场景。
ThreadLocal通过隐式的在不同线程内创建独立实例副本避免了实例线程安全的问题。
每个线程持有一个只属于自己的专属Map并维护了ThreadLocal对象与具体实例的映射, 该Map由于只被持有它的线程访问,故不存在线程安全以及锁的问题。
ThreadLocalMap的Entry对ThreadLocal的引用为弱引用,避免了ThreadLocal对象无法被回收的问题。
都会通过expungeStaleEntry,cleanSomeSlots,replaceStaleEntry这三个方法回收键为null的Entry对象的值,从而防止内存泄露