目录
今天看一个面试题,threadlocal的原理
首先,要知道threadlocal是什么,如果都不知道这个是什么,那就也别看什么原理了.....
threadlocal 是线程的内部存储类,可以在指定线程内存储数据,且只有指定线程可以拿到数据
说白了,就是为了数据安全
不同的线程在使用 threadlocal 的时候,都会有自己的一个 threadlocalmap 对象,并且通过 threadlocal 进行管理
每个新线程都会被实例化一个 threadlocalmap,并且赋值给 threadLocals,如果已经存在了 threadLocals,那就直接复用

那种作用域是线程,并且不同的线程还会有不同的副本的时候,就可以考虑用 threadlocal
当然,如果说业务处理逻辑强依赖与 副本变量,那可能就不大适合这玩意了
这一块我们可以直接看源码就行
- 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 为每个 thread 都维护了一个数组 table,threadllcal 确定了一个数组下标,而这个下标是value存储的对应位置

从之前的 set 源码,我们可以得到,ThreadLocalMap 是一个延迟创建的结构,也就是说,至少要放置一个内容时才会创建
而通过创建 ThreadLocalMap 的源码,我们可以得到,ThreadLocalMap 初始化时创建了默认长度是16的 Entry 数组。通过 hashCode 与 length 位运算确定索引值 i
我们来看看 threadLocalMap 的 set 方法


get 方法没有什么可看的,就直接取就完事了
我们再看看 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,否则永远清除不掉,时间长了就会出问题了