ThreadLocal
是什么
ThreadLocal是Thread的局部变量,用于编多线程程序,对解决多线程程序的并发问题有一定的启示作用。每个线程都可以通过set()和get()来对这个局部变量进行操作,但不会和其他线程的局部变量进行冲突,实现了线程的数据隔离。
ThreadLocal源码
public class ThreadLocal<T> {
private final int threadLocalHashCode = nextHashCode();
//AtomicInteger保证了nextHashCode自增的原子性
private static AtomicInteger nextHashCode = new AtomicInteger();
private static final int HASH_INCREMENT = 1640531527;
private static int nextHashCode() {
return nextHashCode.getAndAdd(1640531527);
}
protected T initialValue() {
return null;
}
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
return new ThreadLocal.SuppliedThreadLocal(supplier);
方法:
- get() 用来获取ThreadLocal在当前线程中保存的变量副本。
- set() 设置当前线程中变量的副本。
- remove() 移除当前线程中变量的副本。
- initialValue()是一个protected方法,一般是用来在使用时进行重写的
ThreadlocalMap
- 是用来存储当前线程的变量,是内部类
- 初始容量16,负载因子2/3
static class ThreadLocalMap {
private static final int INITIAL_CAPACITY = 16;
//存放数据的table
private ThreadLocal.ThreadLocalMap.Entry[] table;
//数组里面entrys的数量
private int size = 0;
// 进行扩容的阈值,表使用量大于它的时候进行扩容
private int threshold; //默认是0
存储结构——Entry
/**
* Entry继承WeakReference,并且用ThreadLocal作为key.如果key为null
* (entry.get() == null)表示key不再被引用,表示ThreadLocal对象被回收
* 因此这时候entry也可以从table从清除。
*/
//弱引用避免因为线程得不到销毁导致ThreadLocal对象无法被回收
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
this.value = v;
}
}
set
//set 方法
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//实际存储的数据结构类型
ThreadLocalMap map = getMap(t);
//如果存在map就直接set,没有则创建map并set
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
//getMap
ThreadLocalMap getMap(Thread t) {
//thred中维护一个ThreadLocalMap
return t.threadLocals;
}
//createMap
void createMap(Thread t, T firstValue) {
//实例化一个新的ThreadLocalMap,并赋值给线程的成员变量threadLocals
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
获取当前线程,并获取当前线程的ThreadLocalMap实例
如果获取到的map实例不为空,调用map.set()方法,否则调用构造函数 ThreadLocal.ThreadLocalMap(this, firstValue)实例化map。
get
public T get() {
Thread t = Thread.currentThread();
ThreadLocal.ThreadLocalMap map = this.getMap(t);
if (map != null) {
ThreadLocal.ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
T result = e.value;
return result;
}
}
return this.setInitialValue();
}
setInitialValue()
private T setInitialValue() {
T value = this.initialValue();
Thread t = Thread.currentThread();
ThreadLocal.ThreadLocalMap map = this.getMap(t);
if (map != null) {
map.set(this, value);
} else {
this.createMap(t, value);
}
if (this instanceof TerminatingThreadLocal) {
TerminatingThreadLocal.register((TerminatingThreadLocal)this);
}
return value;
}
map不为空,就设置键值对,为空,再创建Map
特点
- ThreadLocalMap的数据结构仅仅是数组
- ThreadLocalMap 是通过开放地址法来解决hash 冲突的问题
- ThreadLocalMap里面的Entry 内部类中的key 是弱引用,value 是强引用
应用场景
- Session管理
- 数据库session
- Cookie和Session
内存泄漏
ThreadLocalMap使用ThreadLocal的弱引用作为Entry的key,如果一个ThreadLocal没有外部强引用来引用它,下一次系统GC时,这个ThreadLocal必然会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value
解决方法:
为什么用ThreadLocal作为key
每一个ThreadLocal对象都可以由threadLocalHashCode属性唯一区分或者说每一个ThreadLocal对象都可以由这个对象的名字唯一区分。
为什么要用弱引用
使用弱引用,可以将ThreadLocal对象的生命周期和线程生命周期解绑,持有对ThreadLocal的弱引用,可以使得ThreadLocal在没有其他强引用的时候被回收掉,这样可以避免因为线程得不到销毁导致ThreadLocal对象无法被回收。