ThreadLocal 简单解析
基于jdk1.8
ThreadLocal一定不陌生,开发中常用,也是面试里的常客了,但是往往我们可能只是知道该类的作用。学习该类对于个人的多线程编码能力是大有裨益的,同时也可以知道使用中需要注意的地方
ThreadLocal也叫本地线程变量,我们知道ThreadLocal为每个线程创建一个副本,线程隔离的。
主要有4个公共方法get、remove、set、withInitial
看看set和remove 有set,get、remove就没必要细看了一看就明白了。
1.set
public void set(T value) { Thread t = Thread.currentThread();//获取当前线程、也是能隔离的本质 ThreadLocalMap map = getMap(t);//getMap比较简单 return t.threadLocals; 后面再看这个t.threadLocals if (map != null) map.set(this, value); //好说不就是类似map的put值么,但是注意key是this写死了,表明一个ThreadLocal对象只能存一个值 else createMap(t, value); //空 new ThreadLocalMap(this, firstValue) } t.threadLocals明显是线程类Thread中的变量 ThreadLocal.ThreadLocalMap threadLocals = null; 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; } } private static final int INITIAL_CAPACITY = 16; private Entry[] table; private int threshold; //通过createMap初始化来看 ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY];//16的Entry数组 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);//数组位置 table[i] = new Entry(firstKey, firstValue);//放值 size = 1; setThreshold(INITIAL_CAPACITY);//阀值16*2/3 容量的2/3大小 } } ThreadLocalMap明显就是一个简化版的hashmap,数据结构是Entry数组,每次2倍扩容,没有链表红黑树,其实现了自己的寻址,我想看懂这些应该是不难的。 这里也能看出Thread、ThreadLocal、ThreadLocalMap的关系 Thread存在一个ThreadLocalMap,ThreadLocalMap是一个类型Map的结构可以存储多个ThreadLocal
ThreadLocalMap与WeakReference也理解下,ThreadLocalMap内部存储单元Entry继承WeakReference,是弱引用
弱引用:如果一个对象只具有弱引用,那么垃圾回收器在扫描到该对象时,无论内存充足与否,都会回收该对象的内存。
这么做主要是为了内存回收考虑,当我们用普通线程没问题,线程用完自己会关闭释放内存,但是线程池呢,线程一直存活,是不是意味着这个Map一直存在,但是换任务了等等,其中很多的存储都会是废值,
程序执行时间长了,就可能引发OOM内存泄漏问题,特别是我们的value再比较大。因此用了弱引用,内存不够GC时会干掉key,但是value却还是存在的,而ThreadLocalMap的set、get、remove方法操作元素时会检查null
的key然后释放value,就解决这个问题。但也不绝对,如果ThreadLocal实例我们置null,那ThreadLocal就会回收,但是Map就会一直存在,也可能引发OOM。
map.set private void set(ThreadLocal<?> key, Object value) { // We don't use a fast path as with get() because it is at // least as common to use set() to create new entries as // it is to replace existing ones, in which case, a fast // path would fail more often than not. 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)]) { ThreadLocal<?> k = e.get(); if (k == key) { e.value = value; return; } if (k == null) { replaceStaleEntry(key, value, i);//这里是替换null key的 return; } } tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold)//这里就是清理null key的 rehash(); }
其它没什么多的看的了
总结:需要明白Thread、ThreadLocal、ThreadLocalMap的关系。
ThreadLocalMap是线程的成员变量,不同的线程有不同的ThreadLocalMap,ThreadLocalMap中存储不同的ThreadLocal,实际简单说就是一个线程可以创建多个本地线程变量存多个值且只属于本线程。
WeakReference弱引用往往是面试中问到ThreadLocal类常会深入到的一个点,要知道怎么回事。正常场景ThreadLocal如果我们只是简单的创建,不自己置null,或者创建了ThreadLocal又不用等待操作也不会OOM,正常操作完全没什么大问题。