ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

threadLocal源码分析

2021-12-09 16:31:37  阅读:151  来源: 互联网

标签:分析 key int value threadLocal 源码 len tab null


threadLocal源码分析

threadLocal.set()方法

	public void set(T value) {
        // 获取当前线程
        Thread t = Thread.currentThread();
        // 获取当前线程的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    
	void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

通过此方法我们可以看到ThreadLocalMap是被每个线程独立拥有的,每个线程持有一个map的引用,这也是ThreadLocal实现线程隔离的思想

这样就把要保存数据的生命周期交给了thread自己管理,thread销毁的同时,保存的数据也一起跟着被销毁

threadLocalMap构造

	class ThreadLocalMap {

       static class Entry extends WeakReference<ThreadLocal<?>> {
         	Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
        
        private static final int INITIAL_CAPACITY = 16;

        private Entry[] table;
        
        private int size = 0;
        
        private int threshold;
     }

可以看出这并不是我们平常所看到的map,是通过table数组保存entry对象,同时每个entry对象保存相应的key-value

如何把threadLocal保存到map

思考以下问题:

  1. 怎么计算数组下标?
  2. 两个元素插入时候数组下标相同时,如何处理这种冲突?

带着问题的开始寻找答案

threadLocalMap如何解决hash冲突

这里使用了线性探测法解决hash冲突

看threadLocalMap的set方法:

	private void set(ThreadLocal<?> key, Object value) {
            Entry[] tab = table;
            int len = tab.length;
            // 通过位运算计算数组下标,位运算保证i的值小于等于len-1
            int i = key.threadLocalHashCode & (len-1);

            /**
             * 插入的时候发生了hash冲突
             */
            for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();
                /**
                 * 说明key已经存在了,直接覆盖就行了
                 */
                if (k == key) {
                    e.value = value;
                    return;
                }

                /**
                 * 数组元素不等于null,但是key等于null,说明ThreadLocal弱引用已经被回收
                 * 但是value依然存在,需要释放该元素同时插入新的元素
                 */
                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
                // 如果上面两个条件都不满足
                // 继续寻找下一个null的槽位,i的值一直在变化
            }

            /**
             * 没有产生hash冲突的话,就直接在该数组位置插入
             */
            tab[i] = new Entry(key, value);
            int sz = ++size;
            // 双重判断
            // 1 首先判断是否有可以被回收的节点
            // 2 判断size是否超过了临界值
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

threadLocalMap如何重新计算元素的位置

我们先看map删除一个元素都做了什么操作,然后一步步分析

	private int expungeStaleEntry(int staleSlot) {
            Entry[] tab = table;
            int len = tab.length;

            //1 expunge entry at staleSlot
            tab[staleSlot].value = null;
            tab[staleSlot] = null;
            size--;

            // Rehash until we encounter null
            Entry e;
            int i;
            //2 开始整理工作
            for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) {
                ThreadLocal<?> k = e.get();
                /** 3
                 * 开始清理工作,说明这个threadlocal已经被gc回收了
                 * 但是value作为强引用依然没有被回收,这里需要进行一个清理工作
                 */
                if (k == null) {
                    e.value = null;
                    tab[i] = null;
                    size--;
                } else {
                    int h = k.threadLocalHashCode & (len - 1);
                    //4 把e放到它正真应该存在的位置去
                    if (h != i) {
                        // 释放当前槽位
                        tab[i] = null;

                        // Unlike Knuth 6.4 Algorithm R, we must scan until
                        // null because multiple entries could have been stale.
                        // 发现本属于自己位置还是依然被占用着
                        // 继续寻找插入e的位置(用开放寻址法来解决散列冲突)
                        // 5
                        while (tab[h] != null)
                            h = nextIndex(h, len);
                        // 6
                        tab[h] = e;
                    }
                }
            }
            return i;
        }
  1. 把相应的元素位置置为null,删除该元素
  2. 遍历所有不为null的元素
  3. 如果发现元素的key==null说明threadLocal引用已经被回收,需要把该元素置为null,等待下次垃圾回收
  4. 如果key不等于null,查看元素所处的位置和计算的hash是否是一致的,如果不一致,重新查找它的正确位置
  5. 如果正确的位置已经被占用了,那就继续查找空闲的位置
  6. 找到空闲位置之后赋值

标签:分析,key,int,value,threadLocal,源码,len,tab,null
来源: https://blog.csdn.net/u012886301/article/details/121824521

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有