目录
今天看一个面试题,threadlocal的原理
首先,要知道threadlocal是什么,如果都不知道这个是什么,那就也别看什么原理了.....
ThreadLocal
threadlocal 是线程的内部存储类,可以在指定线程内存储数据,且只有指定线程可以拿到数据
说白了,就是为了数据安全
不同的线程在使用 threadlocal 的时候,都会有自己的一个 threadlocalmap 对象,并且通过 threadlocal 进行管理
每个新线程都会被实例化一个 threadlocalmap,并且赋值给 threadLocals,如果已经存在了 threadLocals,那就直接复用
应用场景
那种作用域是线程,并且不同的线程还会有不同的副本的时候,就可以考虑用 threadlocal
当然,如果说业务处理逻辑强依赖与 副本变量,那可能就不大适合这玩意了
get与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);
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
可以看到,无论是 get 还是 set,都是用的 ThreadLocalMap 的方法
ThreadLocalMap
ThreadLocalMap 为每个 thread 都维护了一个数组 table,threadllcal 确定了一个数组下标,而这个下标是value存储的对应位置
从之前的 set 源码,我们可以得到,ThreadLocalMap 是一个延迟创建的结构,也就是说,至少要放置一个内容时才会创建
而通过创建 ThreadLocalMap 的源码,我们可以得到,ThreadLocalMap 初始化时创建了默认长度是16的 Entry 数组。通过 hashCode 与 length 位运算确定索引值 i
ThreadLocalMap.set
我们来看看 threadLocalMap 的 set 方法
ThreadLocalMap.get
get 方法没有什么可看的,就直接取就完事了
ThreadLocalMap.remove
我们再看看 remove 方法
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
e.clear();
expungeStaleEntry(i);
return;
}
}
}
可以看到,也没什么东西
线程隔离
threadlocal 拥有线程隔离特性,只有线程内可以访问
类比 synchronized,它是通过线程等待来解决访问冲突问题
而 threadlocal 是通过空间存储来解决访问冲突问题
内存泄漏
注意,threadlocal 有一个地方需要注意,那就是内存泄漏的问题,使用完,一定要 remove,否则永远清除不掉,时间长了就会出问题了