综述:每个thread有个threadlocalmap对象 ,这个map对象用来存储此thread中的所有的threadlocal对象,每个threadlocal对象包含一个独一无二的threadlocalhashcode值
1.关联类
ThreadLocal: 线程局部变量
Thread:线程对象
2. Thread与ThreadLocal如何关联?
ThreadLocal类:
- /**
- * Variant of set() to establish initialValue. Used instead
- * of set() in case user has overridden the set() method.
- *
- * @return the initial value
- */
- private T setInitialValue() {
- T value = initialValue();
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null)
- map.set(this, value);
- else
- createMap(t, value);
- return value;
- }
- /**
- * Create the map associated with a ThreadLocal. Overridden in
- * InheritableThreadLocal.
- *
- * @param t the current thread
- * @param firstValue value for the initial entry of the map
- * @param map the map to store.
- */
- void createMap(Thread t, T firstValue) {
- t.threadLocals = new ThreadLocalMap(this, firstValue);
- }
Thread类:
- /* ThreadLocal values pertaining to this thread. This map is maintained
- * by the ThreadLocal class. */
- ThreadLocal.ThreadLocalMap threadLocals = null;
在每一个线程中都声明了一个成员变量ThreadLocalMap,而它的创建则是在ThreadLocal中通过createMap实现。第一次直接通过构造方法保存对象,之后取得ThreadLocalMap的引用进行set,保存在entry中。
3.一个ThreadLocal保存一个线程变量
- /**
- * ThreadLocals rely on per-thread linear-probe hash maps attached
- * to each thread (Thread.threadLocals and
- * inheritableThreadLocals). The ThreadLocal objects act as keys,
- * searched via threadLocalHashCode. This is a custom hash code
- * (useful only within ThreadLocalMaps) that eliminates collisions
- * in the common case where consecutively constructed ThreadLocals
- * are used by the same threads, while remaining well-behaved in
- * less common cases.
- */
- private final int threadLocalHashCode = nextHashCode();
- /**
- * Sets the current thread's copy of this thread-local variable
- * to the specified value. Most subclasses will have no need to
- * override this method, relying solely on the {@link #initialValue}
- * method to set the values of thread-locals.
- *
- * @param value the value to be stored in the current thread's copy of
- * this thread-local.
- */
- public void set(T value) {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null)
- map.set(this, value);
- else
- createMap(t, value);
- }
threadLocalHashCode是ThreadLocal的唯一标识,ThreadLocalMap将ThreadLocal做为key值,因此同一个ThreadLocal对应的value将有且仅有一个,并只保存最后一次value值。起初脑袋被门夹了,一直想不明白ThreadLocal无论你set多少次只保存最后一个对象,定义ThreadLocalMap仅仅用来保存一个对象?后来才发现,原来是钻牛角尖了,当前线程下多new 几个ThreadLocal不就行了(学艺不精,思维没打开啊)。
4.ThreadLocalMap中Entry继承了WeakReference
虽然实现了弱引用,在内存不够的时候,即使还被引用,也能被GC回收,但是最好习惯性手动调用下
- /**
- * Remove the entry for key.
- */
- 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时,记得手动调用下remove
5.ThreadLocalMap保存算法
- /**
- * Set the value associated with key.
- *
- * @param key the thread local object
- * @param value the value to be 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);
- return;
- }
- }
- tab[i] = new Entry(key, value);
- int sz = ++size;
- if (!cleanSomeSlots(i, sz) && sz >= threshold)
- rehash();
- }
拿ThreadLocal的threadLocalHashCode与长度减一相与,求出哈希表的位置。如果这个插槽已有不为空的entry,则依次遍历之后的位置,当达到最大长度时,重新从第一个位置开始找,直到找着为null的插槽.增加size记录已有多少个元素,在查找过程中会判断这个entry弱引用是否已被回收,如果已回收则清楚value重新释放这个插糟并size-1,如果size等于加载因子则按2倍扩容。
6.总结
通过Thread.currentThread()取得当前线程,在这个线程中活动期间创建多个ThreadLocal放入当前线程的ThreadLocalMap中,相当于将多线程分开,每个人操作自己的线程,在自己线程中创建与使用自己的变量,各管各的,这就是所谓的“为每一个使用该变量的线程都提供一个变量值的副本,使每一个线程都可以独立地改变自己的副本”。
与synchronized相比只了解了其中之一的优点:“以空间换时间”。
ThreadLocal会随着线程的销毁而销毁。