Java并发之ThreadLocal
前言
ThreadLocal是一个全局变量,用于解决线程范围内变量共享问题,其实ThreadLocal可以看作是一个Map集合,而key就是当前线程名,而value是想要存放的变量,ThreadLocal的变量用完会自动销毁,不用考虑ThreadLocal中的变量会占用空间,对于ThreadLocal来说每个线程的变量都是独立的,不会相互影响,可以通过ThreadLocal在同一线程内的不同组件传递公共变量
成员变量
我们看ThreadLocal的源码可以看到他有三个成员变量
/** 这个属性用于ThreadLocal的桶位寻址 如果一个threadLocal对象第一次调用get()方法,那么就会给当前线程分配一个value 这个value和这个threadLocal对象被包装成一个entry 这个entry的key是threadLocal对象,value是生成的value **/ private final int threadLocalHashCode = nextHashCode(); /** 这个属性是创建ThreadLocal会用到该属性 每创建一个ThreadLocal对象,就会使用该属性分配一个hash值给这个对象 **/ private static AtomicInteger nextHashCode = new AtomicInteger(); /** 表示hash增量,没创建一个ThreadLocal对象,ThreadLocal.nextHashCode就会增长HASH_INCREMENT **/ private static final int HASH_INCREMENT = 0x61c88647;
get方法
public T get() { //获取当前线程 Thread t = Thread.currentThread(); //根据当前线程获取ThreadLocalMap对象 ThreadLocalMap map = getMap(t); //判断map是否为null if (map != null) { //当前threadLocal对象作为key获取entry ThreadLocalMap.Entry e = map.getEntry(this); //判断entry是否为null if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; //返回result return result; } } //初始化 return setInitialValue(); }
接下来我们再看这个初始化方法
setInitialValue方法
这个方法是用于初始化的,我们从上面的get方法可以知道,进入该方法有两种情况,一个就是ThreadLocalMap为null或者Entry为null
private T setInitialValue() { //调用ThreadLocal对象的方法进行初始化 T value = initialValue(); //获取到当前线程 Thread t = Thread.currentThread(); //当前线程作为key获取ThreadLocalMap对象 ThreadLocalMap map = getMap(t); //判断map是否为null,如果不为null证明已经初始化过ThreadLocalMap对象了(一个ThreadLocal只会初始化一次) if (map != null) //向ThreadLocalMap中保存当前threadLocal与当前线程生成的线程局部变量 map.set(this, value); else //如果走到这里证明map==null,进行初始化创建一个ThreadLocalMap对象 createMap(t, value); return value; }
set方法
public void set(T value) { //获取当前线程 Thread t = Thread.currentThread(); //将当前线程作为key获取ThreadLocalMap对象 ThreadLocalMap map = getMap(t); //判断threadLocalMap是否为null if (map != null) //覆盖或者添加新的key - value map.set(this, value); else //证明还没有被初始化,进行初始化 createMap(t, value); }
如何实现线程隔离?
每一个Thread都有着自己的ThreadLocalMap用于存储数据,当线程访问某个ThreadLocal对象的get方法的时候,方法内部会判断该线程的ThreadLocalMap数组内是否存在以当前线程为key的Entry节点,如果数组内没有对应的节点,那么当前ThreadLocal对象,就会调用初始化方法,创建一个Entry节点放入ThreadLocalMap中
ThreadLocal内存泄漏原因
主要是因为ThreadLocalMap的Entry对象是弱引用,对于弱引用,JVM在每次GC的时候都会清理他们,此时key就会变为null,如果此时没有其他强引用指向value,那么此时value就不会被访问到,按道理来说value应该也会被GC,但是此时ThreadLocalMap的Entry对象对value是强引用,这样就会导致JVM始终无法GC value,这样就导致了value成了一个始终无法清理的value
#Java##程序员#