ThreadLocal核心原理
ThreadLocal
定义
ThreadLocal叫做线程变量,这个变量对其他线程而言是隔离的,是当前线程独有的变量。ThreadLocal为变量在每个线程中都创建了一个副本,每个线程可以访问自己内部的副本变量。
ThreadLocal与Synchronized的区别
1、Synchronized用于线程间的数据共享,ThreadLocal用于线程间的数据隔离。
2、Synchronized是利用锁的机制,让变量或代码块在某一时该只能被一个线程访问,用于在多个线程间通信时能够获得数据共享。ThreadLocal为每一个线程都提供了变量的副本,让每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。
底层实现
在 Thread 类中嵌入一个 ThreadLocalMap,ThreadLocalMap 就是一个容器,存储的就是这个 Thread 类专享的数据。
ThreadLocalMap底层结构
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
……
}
ThreadLocal在保存的时候会把自己当做Key存在ThreadLocalMap中,key被设计成WeakReference弱引用了。
ThreadLocalMap的key设计成弱引用,主要是为了避免内存泄漏的情况。如果 threadlocalmap 的 key 是强引用, 那么只要线程存在, threadlocalmap 就存在, 而 threadlocalmap 结构就是 entry 数组. 即对应的 entry 数组就存在, 而 entry 数组元素的 key 是 threadLocal.即便我们在代码中显式赋值 threadlocal 为 null, 告诉 gc 要垃圾回收该对象. 由于上面的强引用存在, threadlocal 即便赋值为 null, 只要线程存在, threadlocal 并不会被回收。
而设置为弱引用, gc 扫描到时, 发现ThreadLocal在没有外部强引用时,发生GC时会被回收,如果创建ThreadLocal的线程一直持续运行,那么这个Entry对象中的value就有可能一直得不到回收,发生内存泄露。所以在代码最后都需要用remove把值清空。
remove的源码很简单,找到对应的值全部置空,这样在垃圾回收器回收的时候,会自动把他们回收掉。
并且 threadlocal 的 set get remove 都会判断是否 key 为 null, 如果为 null, 那么 value 的也会移除, 之后会被 gc 回收。
结构大致这样:
ThreadLocalMap存储元素的过程
ThreadLocalMap在存储的时候会给每一个ThreadLocal对象一个threadLocalHashCode,在插入过程中,根据ThreadLocal对象的hash值,定位到table中的位置,如果当前位置是空的,就初始化一个Entry对象放在位置上。如果位置不为空,如果这个Entry对象的key正好是即将设置的key,那么就刷新Entry中的value。如果位置不为空,而且key不等于entry,那就找下一个空位置,直到为空为止。在get的时候,也会根据ThreadLocal对象的hash值,定位到table中的位置,然后判断该位置Entry对象中的key是否和get的key一致,如果不一致,就判断下一个位置。这种方式在不使用链表的情况下,解决了hash冲突。
ThreadLocal实现线程隔离的原理
ThreadLocal实现线程隔离主要是设置值和获取值的时候,就已经保证了它是线程隔离了。
设置值的代码:
public void set(T value) {
Thread t = Thread.currentThread();// 获取当前线程
ThreadLocalMap map = getMap(t);// 获取ThreadLocalMap对象
if (map != null) // 校验对象是否为空
map.set(this, value); // 不为空set
else
createMap(t, value); // 为空创建一个map对象
}
设置值先是获取当前线程对象,然后从当前线程中获取线程的ThreadLocalMap,判断这个对象是不是空的,如果是空的,就创建一个空的map对象,如果不为空,就重新设值。key就是当前ThreadLocal 的对象,值是添加到这个ThreadLocalMap中的,它是存储在线程内部,然后关联了对应的ThreadLocal。
ThreadLocalMap是当前线程Thread一个叫threadLocals的变量中获取的
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
public class Thread implements Runnable {
……
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
……
每个线程Thread都维护了自己的threadLocals变量,所以在每个线程创建ThreadLocal的时候,实际上数据是存在自己线程Thread的threadLocals变量里面的,别人没办法拿到,从而实现了隔离。
通过ThreadLocal.get 时就能获取到对应的值。
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();
}
- 点赞
- 收藏
- 关注作者
评论(0)