初探ThreadLocal

一、ThreadLocal是干什么的

ThreadLocal是用来在多线程时存储线程内部的局部变量,各个线程之间的变量是独立的不会相互影响。(个人理解为以当前线程为key,以变量为value存储变量)

二、基本使用

空参构造,创建ThreadLocal对象。
public void set(T value)设置当前线程绑定的局部变量
public T get()获取当前线程绑定的局部变量
public void remove() 移除当前线程绑定的局部变量
protected T initialValue()返回当前线程的初始值

三、ThreadLocal和synchronized的区别

synchronized也能达到一样的结果,但是当并发数量很多时,由于加锁了,所以性能会降低!synchronized是用锁来保证,而ThreadLocal可以理解为线程的map.

四、ThreadLocal源码分析

JDK早期的ThreadLocal是ThreadLocal维护了Map,线程Thread作为key,变量为value。但是现在JDK1.8中每个Thread维护一个ThreadLocalMap,key是这个ThreadLocal实例本身,value是真正的值。
具体过程:1.每个Thread线程内部都有一个Map 2.Map存储ThreadLocal对象(key),和线程的变量副本(value)。3.Thread内部的Map是由ThreadLocal维护的,由ThreadLocal负责向map获取和设置线程的变量值。4. 对于不同的线程,每次获取副本值时,别的线程并不能获取到当前线程的副本值,形成了副本的隔离,互不干扰。

这样的好处在于:1. 每个Map存储的Entry变少(因为原来的Entry数量由线程数决定,而现在由ThreadLocal决定) 2.当Thread销毁时,ThreadLocal也会销毁,释放内存。

set方法源码分析->先获取当前线程,之后获取当前线程中的ThreadLocalMap对象,如果存在则调用set(ThreadLocal,value),如果不存在则创建Map并放入(在创建时候就放入,这是一步)。
set(ThreadLocal,value)通过key找出索引,查找该位置的Entry,如果存在且等于传入的key,那么重新赋值,如果key为null,新的替换掉null。如果不为null则不断循环到下一个索引检测直到遇到null。

get方法源码分析->先获取当前线程,获取当前线程中的ThreadLocalMap对象,先判断这个对象是否为null,不为null时map.getEntry(this)获取实体,之后判断得到的实体是否为空,不为空时返回value。如果map不存在或没有相应的实体则调用setInitialValue()方法,将引用的ThreadLocal引用和value作为firstkey和firstvalue创建一个新的map。

remove方法源码分析->先获取当前线程,并获取map,当获取的map不为空时,移除当前ThreadLocal对象对应的entry

InitialValue()->返回该线程局部变量的初始值,如果没有重写则直接返回null。

ThreadLocal的初始容量16,存储结构Entry继承自WeakReference,也就是key是弱引用,目的是将ThreadLocal对象的生命周期和线程生命周期解绑。

五、内存泄漏

内存泄漏是指程序中分配的堆内存没有被释放,造成内存的浪费。内存泄漏的堆积导致内存溢出。

产生内存泄漏的原因有两个:1. 没有手动删除这个Entry 2.当前线程依然在运行
内存泄漏的根源在于:ThreadLocalMap的生命周期与Thread一样长,如果没有手动删除对应key就会导致内存泄漏。

六、ThreadLocalMap如何解决hash冲突

hash值计算时采用了一个 (黄金分割数&(容器大小-1))保证冲突次数减少。

你可能感兴趣的:(java,多线程,内存泄漏)