ThreadLocal可以实现不同线程公用一个ThreadLocal,每个线程都可以通过同一个ThreadLocal操作自己的数据,而不会出现线程安全问题,ThreadLocal本身没有存储数据功能,是通过其静态内部类ThreadLocalMap实现存储线程的数据
ThreadLocalMap重要的内容有:
1.静态内部类:Entry,是用于存储每一个ThreadLocal(key)-value数据的类
static class Entry extends WeakReference<ThreadLocal<?>> {Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}
}
ThreadLocalMap的Entry是继承了WeakReference<ThreadLocal<?>,使用ThreadLocal的弱引用作为key,当保存的ThreadLocal obj的内存对象没有一个强引用指向它时,这个内存对象就会被gc回收,因此会造成Entry中的ThreadLocal为null,而value是强引用,不会被回收,当时key为null,则该entry是无意义的,value就形成了内存泄漏。
弱引用:一个内存对象只有弱引用指向它,这个内存对象是可以被gc回收的
每次使用完ThreadLocal,都调用它的remove()方法,清除数据,防止内存泄漏
2.属性:
private Entry[] table;//存放数据的数组,初始大小为16,每一个entry存放的是ThreadLocal 和 value
private int size = 0;//表示当前table中存放的元素个数,即有几个Entry
private int threshold; // rehash阈值,当size>=threshold则会进行rehash
3.创建ThreadLocalMap。Thread对象中有一个ThreadLocalMap threadLocals 属性:
(一)ThreadLocal保存数据时
1.获取当前线程对象的ThreadLocalMap(见下方ThreadLocalMap map = getMap(t))
2.把当前的ThreadLocal对象和数据value保存在thread对象的ThreadLocalMap的
Entry[] table中
(二)ThreadLocal取数据时
1.获取当前线程对象的ThreadLocalMap
2.然后根据ThreadLocal对象(key)找到Entry,最后将数据(value)取出来
这就是为什么在当前线程中使用ThreadLocal只能取到该线程放进去的数据。
ThreadLocal th = new ThreadLocal();
1.*th.createMap(Thread t, T firstValue);void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}2.*th.get(); public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);//先获取当前线程的ThreadLocalMap if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);//根据当前ThreadLocal获取Entryif (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;//获取数据valuereturn result;}}return setInitialValue();}private T setInitialValue() {T value = initialValue();Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);//实际上还是调用createMap(t, value);,t是当前线程,value为nullreturn value;}
3.*t.set(T value) public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);//实际上还是调用createMap(t, value);,t是当前线程}
4.(ThreadLocal)set(T value) 保存数据到当前线程中
public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);//实际上还是调用createMap(t, value);,t是当前线程
}map.set(this, value):
private void set(ThreadLocal<?> key, Object value) {Entry[] tab = table;int len = tab.length;int i = key.threadLocalHashCode & (len-1);//由hash值&len-1找到key在table中的下标ifor (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {nextIndex(i, len)寻找i下一位,i==len-1时,下一位时0ThreadLocal<?> k = e.get();if (k == key) {//找到位置,设置替换value后退出e.value = value;return;}if (k == null) {replaceStaleEntry(key, value, i);return;}}//for之后,要么是key第一次设置,要么是出现hash冲突,i位置处Entry==nulltab[i] = new Entry(key, value);//将key放置在i位置处int sz = ++size;//元素个数++if (!cleanSomeSlots(i, sz) && sz >= threshold)//没删除且当前元素个数达到rehash阈值,则执行rehashrehash();
}private boolean cleanSomeSlots(int i, int n) {//i是新加入元素位置,n为元素个数,方法是为了清除那些key值为null的Entry,并进行rehashboolean removed = false;Entry[] tab = table;int len = tab.length;do {i = nextIndex(i, len);//找当前插入Entry的下一位置Entry e = tab[i];if (e != null && e.get() == null) {//如果下一位置有Entry,且Entry保存的ThreadLocal是null,则要删除该Entryn = len;//重置nremoved = true;i = expungeStaleEntry(i);//expungeStaleEntry(i)是清除那些key值为null的Entry,并进行rehash的真正方法}} while ( (n >>>= 1) != 0);//循环删除,直到把所有key为null的Entry删除return removed;//true:删除了,false:没删除
}private int expungeStaleEntry(int staleSlot) {//stalSlot就是要删除的位置,且会删除所有key为null的Entry,并进行rehashEntry[] tab = table;int len = tab.length;// 将当前Entry删除tab[staleSlot].value = null;tab[staleSlot] = null;size--;//元素-1// 从删除位置下一位起开始删除其他key为null的Entry并rehash,直到遇到空的EntryEntry e;int i;for (i = nextIndex(staleSlot, len);//从删除位置下一位起(e = tab[i]) != null;i = nextIndex(i, len)) {ThreadLocal<?> k = e.get();if (k == null) {//key是null则删除e.value = null;tab[i] = null;size--;} else {//key不是null则进行rehashint h = k.threadLocalHashCode & (len - 1);//重新计算hashif (h != i) {//hash与当前位置不同,则表示发生过hash冲突,被迫移动到现在的i位置tab[i] = null;//当前位置设为null,重新找位置// 之前删除了一些空的Entry,因此将那些有冲突的Entry:e重新移到那些被删除位置while (tab[h] != null)//从h开始重新找位置,该位置Entry必须是nullh = nextIndex(h, len);tab[h] = e;}}}return i;//位置i的Entry为null
}//rehash方法:rehash就是出掉那些
private void rehash() {expungeStaleEntries();if (size >= threshold - threshold / 4)//rehash后,元素个数大于table长度1/2,则要扩容resize();
}//rehash
private void expungeStaleEntries() {Entry[] tab = table;int len = tab.length;for (int j = 0; j < len; j++) {Entry e = tab[j];if (e != null && e.get() == null)expungeStaleEntry(j);//该方法就是上面删除空key的Entry并rehash的方法,remove方法也是用的这一个方法}
}//扩容两倍
private void resize() {Entry[] oldTab = table;int oldLen = oldTab.length;int newLen = oldLen * 2;Entry[] newTab = new Entry[newLen];int count = 0;for (int j = 0; j < oldLen; ++j) {Entry e = oldTab[j];if (e != null) {ThreadLocal<?> k = e.get();if (k == null) {e.value = null; // 顺便清除空key的Entry的value} else {int h = k.threadLocalHashCode & (newLen - 1);while (newTab[h] != null)h = nextIndex(h, newLen);newTab[h] = e;count++;}}}setThreshold(newLen);size = count;table = newTab;
}
5.(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();}private T setInitialValue() {T value = initialValue();Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);return value;}
6.(ThreadLocal)remove() :删除方法
public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());if (m != null)m.remove(this);}ThreadLocalMap:
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();//将e中的ThreadLocal==nullexpungeStaleEntry(i);return;}}}