前言
ThreadLocal 是java.lang下的一个类,虽说在多线程中会用到ThreadLocal,然其并不在java.util.concurrent包下。说明其立意就不是为了解决高并发问题。那TreadLocal类可以做啥,ThreadLocal类可以维护每个线程的私有变量,做到线程变量的隔离,就好像一个银行,里面有各种各样的保险柜,每个线程都对应一个单独的保险柜,保险柜存储的就是线程的私有变量,每个保险柜只允许对应的线程访问。
ThreadLocal的使用也很简单
ThreadLocal threadLocal = new ThreadLocal();
new Thread(() ->{
threadLocal.set("thread :" + Thread.currentThread().getName());
System.out.println(threadLocal.get());
}).start();
new Thread(() ->{
threadLocal.set("thread :" + Thread.currentThread().getName());
System.out.println(threadLocal.get());
}).start();
new Thread(() ->{
threadLocal.set("thread :" + Thread.currentThread().getName());
System.out.println(threadLocal.get());
}).start();
简单分析一下demo,初始化一个ThreadLocal对象后,启动三个线程,各自往线程里存放各自线程的名字,然后再调用get方法取出来自己所存放的线程名字。
正文
接下来,看下源码
ThreadLocal有两个内部类,其中最重要的就是ThreadLocalMap
内部类
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
// 初始化map长度
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
代码很简单,默认长度为16,private static final int INITIAL_CAPACITY = 16;
Entry为ThreadLocalMap的内部类
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
// k为threadLocal
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
get方法解析
public T get() {
// 当前线程
Thread t = Thread.currentThread();
// 获取当前线程下的ThreadLcoalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
// 从map中取值
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// map 等于null 就赋个初始值为null
return setInitialValue();
}
所以捋一下思路
- 从当前的线程获取TreadLcoalMap
- map不等于null,从map中去取值
- map等于null,创建map并赋个初始值
看下setInitialValue
方法
private T setInitialValue() {
// 初始值为null
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
// 创建map
createMap(t, value);
return value;
}
protected T initialValue() {
return null;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
setInitialValue这方法也很简单。
set方法解析
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
看过get方法解析的时候,相信读者对set方法也一目了然。就是对内部的ThreadLocalMap进行赋值而已。
内存泄漏问题
虽然有不少读者都了解过ThreadLcoal类,很多读者都认为容易造成内存泄漏问题,原因是ThreadLocalMap里存储着ThreadLcoal的实例,所以在线程池情况下,导致一直无法释放,会造成内存泄漏,实际上Entry这个内部类继续了弱引用WeakReference
这个类,这个类会自动帮你进行决定垃圾回收,和强引用不一样,强引用一定得这个引用方生命周期结束才会被垃圾回收掉,弱引用只需要检测到这个类没有被强引用的话就会放到垃圾回收队列。
后话
本文介绍了一些ThreadLocal的思想及原理,要明白ThreadLocal并不是用来解决高并发的问题。