ThreadLocal源码分析

数据结构

ThreadLocal源码分析_第1张图片

方法源码解读

  • set 方法
    1. 找到当前的map
    2. 如果map不为空, 计算当前对象的hashCode, 把数据保存到table数组中
    3. 如果map为空, 新建一个map
    4. ThreadLocal对象的成员变量保存为新建的map
    5. 计算当前ThreadLocal实例对象的hashCode, 并且把当前的数据保存到table数组中.
public void set(T value) {
        //获取当前线程
        Thread t = Thread.currentThread();
        // 获取map, 该map保存在Thread对象成员变量中
        ThreadLocal.ThreadLocalMap map = getMap(t);
        // 判断map是否为空
        if (map != null)
            // 如果不为空, 
            map.set(this, value);
        else
            // 创建map,并且,把值设置进来
            createMap(t, value);
    }


  • createMap方法
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
			// 初始化table
            table = new Entry[INITIAL_CAPACITY];
            // 计算槽位
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            // 存值
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            // 计算临界值
            setThreshold(INITIAL_CAPACITY);
       
     }

  • threadLocalHashCode 计算方法

这里使用类的一个静态变量,每次新建一个在HASH_INCREMENT的基础上进行累加,从这里保证hashCode值的唯一性,递增的计算,保证槽位不会被占用


private final int threadLocalHashCode = nextHashCode();
private static int nextHashCode() {
		// 使用cas的线程安全的方式进行操作
       return nextHashCode.getAndAdd(HASH_INCREMENT);
   }
private static final int HASH_INCREMENT = 0x61c88647;

private static AtomicInteger nextHashCode = new AtomicInteger();

  • set(ThreadLocal key, Object value) 方法, 这里参考上面数据结构图
/**
 * key, 当前ThreadLocal对象
 * value, 用户设置的值
 */
private void set(ThreadLocal<?> key, Object value) {
			
            Entry[] tab = table;
            int len = tab.length;
            // 计算hashCode值
            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;
                }
				// key为空的处理,
                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
			// 新建一个键值对, 并且加入到table中
            tab[i] = new Entry(key, value);
            int sz = ++size;
            // 检查是否需要对table[]进行扩容, 初始默认大小为16
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

  • get方法
    1. 使用线程找到对象的map
    2. 使用当前ThreadLocal对象hashCode找到其在table中的位置, 取出value值返回
public T get() {
        Thread t = Thread.currentThread();
        // 获取map
        ThreadLocalMap map = getMap(t);
        if (map != null) {
        	// 使用hash计算槽位, 并且获取value值
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

  • setInitialValue(), 方法初始化一个null值来返回
 private T setInitialValue() {
 		// 初始化value值为空 
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            map.set(this, value);
        } else {
            createMap(t, value);
        }
        if (this instanceof TerminatingThreadLocal) {
            TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
        }
        // 最后直接返回这个null值
        return value;
    }
  • remove方法
    1. 通过线程定位到map对象
    2. 通过当前ThreadLocal对象找到table数组中的位置
    3. 清除数据
    4. 重新计算hash
public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null) {
             m.remove(this);
         }
     }

 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;
                }
            }
        }
	
 private int expungeStaleEntry(int staleSlot) {
            Entry[] tab = table;
            int len = tab.length;

            // expunge entry at staleSlot
            // 这里是清除数据的代码
            tab[staleSlot].value = null;
            tab[staleSlot] = null;
            size--;

            // Rehash until we encounter null
            Entry e;
            int i;
            for (i = nextIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
                ThreadLocal<?> k = e.get();
                if (k == null) {
                    e.value = null;
                    tab[i] = null;
                    size--;
                } else {
                    int h = k.threadLocalHashCode & (len - 1);
                    if (h != i) {
                        tab[i] = null;

                        // Unlike Knuth 6.4 Algorithm R, we must scan until
                        // null because multiple entries could have been stale.
                        while (tab[h] != null)
                            h = nextIndex(h, len);
                        tab[h] = e;
                    }
                }
            }
            return i;
        }

总结

ThreadLocal的数据结构相对而言比较简单, 而且它使用起来也非常的简单, 但是也要注意的地方, 在线程执行完毕之前, 需要执行remove方法, 主动删除保存的value对象, 不然会造成内存泄漏问题

你可能感兴趣的:(多线程,java基础,java)