首页 > 编程语言 >ThreadLocal源码解析

ThreadLocal源码解析

时间:2024-03-15 17:24:33浏览次数:19  
标签:解析 tab int staleSlot len ThreadLocal 源码 key null

ThreadLocal

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        //map不为null,之前设置过情况
        map.set(this, value);
    else
        createMap(t, value);
}
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;
    //threadLocal对象的hash值&桶大小对应索引
    int i = key.threadLocalHashCode & (len-1);

    //索引下探,每次获取entry不为空,匹配地址相等情况
    for (Entry e = tab[i];
         e != null;
         e = tab[i = nextIndex(i, len)]) {
        ThreadLocal<?> k = e.get();
		//如果之前这个线程设置过threadlocal对象的值
        //并且地址相等情况就会替换
        if (k == key) {
            e.value = value;
            return;
        }
		//弱引用key被gc清除或者其他情况,但是entry不为null
        if (k == null) {
            replaceStaleEntry(key, value, i);
            return;
        }
    }
	//如果e为空,直接赋值
    tab[i] = new Entry(key, value);
    int sz = ++size;
    //向下扫码移除key=null,并判断是否需要扩容,清除失败,并且,size>=扩容阈值
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
}
private void replaceStaleEntry(ThreadLocal<?> key, Object value,
                               int staleSlot) {
    Entry[] tab = table;
    int len = tab.length;
    Entry e;

    // Back up to check for prior stale entry in current run.
    // We clean out whole runs at a time to avoid continual
    // incremental rehashing due to garbage collector freeing
    // up refs in bunches (i.e., whenever the collector runs).
    int slotToExpunge = staleSlot;
    //向左探测
    for (int i = prevIndex(staleSlot, len);
         //控制条件
         (e = tab[i]) != null;
         i = prevIndex(i, len))
        if (e.get() == null)
            //默认是入参staleSlot
            //向左遍历,找出entry=null之后
            //第一个弱引用key=null,索引设置为slotToExpunge
            //设置清除key=null起始点
            slotToExpunge = i;

    // Find either the key or trailing null slot of run, whichever
    // occurs first
    //向右探测
    for (int i = nextIndex(staleSlot, len);
         (e = tab[i]) != null;
         i = nextIndex(i, len)) {
        ThreadLocal<?> k = e.get();

        // If we find key, then we need to swap it
        // with the stale entry to maintain hash table order.
        // The newly stale slot, or any other stale slot
        // encountered above it, can then be sent to expungeStaleEntry
        // to remove or rehash all of the other entries in run.
        if (k == key) {
            e.value = value;
			//当前匹配k=key地址存入entry不为null,key=null的值,注意tab[i] = tab[staleSlot];为原key=null指向当前i索引地址
            tab[i] = tab[staleSlot];
            //入参staleSlot地址放入新创建entry,key匹配
            tab[staleSlot] = e;

            // Start expunge at preceding stale entry if it exists
            //如果起始点为原入参点,现在被tab[staleSlot] = e;占,那么新的起始点就是i
            if (slotToExpunge == staleSlot)
                slotToExpunge = i;
            cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
            return;
        }

        // If we didn't find stale entry on backward scan, the
        // first stale entry seen while scanning for key is the
        // first still present in the run.
        //向右探测,entry=null前,是否还存在key=null的节点,如果存在并且slotToExpunge == staleSlot,
        //开始清理初始点就为i,只会执行一次
        if (k == null && slotToExpunge == staleSlot)
            slotToExpunge = i;
    }

    // If key not found, put new entry in stale slot
    //同理占用
    tab[staleSlot].value = null;
    tab[staleSlot] = new Entry(key, value);

    // If there are any other stale entries in run, expunge them
    //使用向右探测清理初始点
    if (slotToExpunge != staleSlot)
        cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
}

cleanSomeSlots判断是否存在需要清除,存在即扫描log2(n),给expungeStaleEntry清除快速返回下一个扫描entry!=null索引点

private boolean cleanSomeSlots(int i, int n) {
    boolean removed = false;
    Entry[] tab = table;
    int len = tab.length;
    do {
        //从下一个索引开始
        i = nextIndex(i, len);
        Entry e = tab[i];
        if (e != null && e.get() == null) {
            //如果发现存在key=null,则n设置为len
            //为了在while循环扫描控制:扫描log2(n)个单元格
            n = len;
            //remove标识
            removed = true;
            //返回tab[i]==null的索引,继续探测
            i = expungeStaleEntry(i);
        }
    } while ( (n >>>= 1) != 0);
    return removed;
}

expungeStaleEntry,向右清除缩小探测偏差,返回第一个entry=null索引

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;
    //环形下探索引,直到tab[i]==null
    for (i = nextIndex(staleSlot, len);
         (e = tab[i]) != null;
         i = nextIndex(i, len)) {
        ThreadLocal<?> k = e.get();
        //继续扫描存在key==null,清除
        if (k == null) {
            e.value = null;
            tab[i] = null;
            size--;
        } else {
            //如果key!=null,计算桶初始位置
            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.
                //从初始h下探null桶放置,目的为了减少h索引偏差
                while (tab[h] != null)
                    h = nextIndex(h, len);
                tab[h] = e;
            }
        }
    }
    //返回tab[i]==null的索引
    return i;
}
private void rehash() {
    expungeStaleEntries();
	//清除过后,还判断
    // Use lower threshold for doubling to avoid hysteresis
    //threshold = len * 2 / 3;
    //size>=4/6*len-1/6*len
    //也就是是否大于一半的len
    if (size >= threshold - threshold / 4)
        resize();
}

扩容过程中还会全清理,key=null节点

/**
 * Expunge all stale entries in the table.
 */
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);
    }
}
/**
 * Double the capacity of the table.
 */
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();
            //此处又置空key==null的entry
            if (k == null) {
                e.value = null; // Help the GC
            } else {
                //计算新的hash桶地址
                int h = k.threadLocalHashCode & (newLen - 1);
               	//如果已经有值,继续下探
                while (newTab[h] != null)
                    h = nextIndex(h, newLen);
                newTab[h] = e;
                count++;
            }
        }
    }
	//计算新的扩容阈值
    setThreshold(newLen);
    size = count;
    table = newTab;
}

标签:解析,tab,int,staleSlot,len,ThreadLocal,源码,key,null
From: https://www.cnblogs.com/wsyphaha/p/18075860

相关文章

  • [原创] KCP 源码分析(上)
    KCP协议是一种可靠的传输协议,对比TCP取消了累计确认(延迟ACK)、减小RTO增长速度、选择性重传而非全部重传。通过用流量换取低时延。KCP中最重要的两个数据结构IKCPCB和IKCPSEG,一个IKCPCB对应一个KCP连接,通过这个结构体维护发送缓存、接收缓存、超时重传时间、窗口大小等。I......
  • JavaScript逆向之iwencai请求头参数加密过程解析
    iwencai网站实战url:http://iwencai.com/unifiedwap/home/index打开开发者工具,在搜索框中随便输入关键词,看流量包。(如果想将开发者工具的位置进行变换的,可以点击三个点进行切换)只有一条数据包,看看请求数据和响应数据。请求头中有一个特殊的Hexin-V,它的值与Cookie中的v的值......
  • 全套大型体检中心PEIS源码 医院PEIS管理系统源码
    大型体检中心PEIS源码 医院PEIS管理系统源码医院智慧体检系统,该系统覆盖医院体检科、体检中心的所有业务,完成从预约、登记、收费、检查、检验、出报告、分析、报表等所有工作。系统可以对团检的每个环节设有操作界面,从检前的预约、记录、EXCEL数据批量导入、自动筛选、自......
  • 中电金信:技术实践|Flink维度表关联方案解析
    ​导语:Flink是一个对有界和无界数据流进行状态计算的分布式处理引擎和框架,主要用来处理流式数据。它既可以处理有界的批量数据集,也可以处理无界的实时流数据,为批处理和流处理提供了统一编程模型。 维度表可以看作是用户来分析数据的窗口,它区别于事实表业务真实发生的数据,通常......
  • Django admin管理工具的使用、定制及源码解析
    Djangoadmin管理工具的使用、定制及源码解析admin组件使用Django提供了基于web的管理工具。Django自动管理工具是django.contrib的一部分。你可以在项目的settings.py中的INSTALLED_APPS看到它:#ApplicationdefinitionINSTALLED_APPS=['django.contrib.a......
  • 小程序开发平台源码系统:万能建站门店小程序功能 带完整的搭建教程以及代码包
    在移动互联网时代,小程序以其独特的优势,迅速占领了市场的一席之地。然而,对于许多中小企业和个人开发者来说,缺乏专业的开发团队和技术支持,使得小程序开发成为一项难以逾越的技术门槛。小编给大家分享一款万能建站门店小程序源码系统,旨在降低小程序开发的难度,让更多的人能够轻松搭......
  • 团购小程序源码系统:快递代收+社区便利店+推送商品等功能 带完整的安装部署教程
    在移动互联网高速发展的今天,小程序以其轻便、快捷、无需下载的特点,迅速成为商家与用户之间的桥梁。为了满足社区团购市场的需求,小编给大家分享一款功能强大的团购小程序源码系统,该系统集成了快递代收、社区便利店、推送商品等多项功能,为商家提供了一个高效、便捷的运营平台。......
  • 微信小程序 代驾预约评价系统 uniapp毕业设计源码lw
    代驾系统的系统项目的概述设计分析,主要内容有平台的具体分析,进行数据库的是设计,数据采用mysql数据库,并且对于系统的设计采用比较人性化的操作设计,对于系统出现的错误信息可以及时做出处理及反馈。本代驾服务系统主要包括系统用户管理模块、代驾线路信息管理、车辆信息管理、代......
  • 【计算机网络】域名劫持无处遁形:基于HTTPDNS打造可靠且安全的域名解析体系
    ......
  • powerbuilder11.5解析XML
    //定义变量OLEObjectlole_xmlhttpOLEObjectlole_xml//创建MSXML2.XMLHTTP对象lole_xmlhttp=CREATEOLEObjectlole_xmlhttp.ConnectToNewObject("MSXML2.XMLHTTP")//创建MSXML2.DOMDocument对象lole_xml=CREATEOLEObjectlole_xml.ConnectToNewObject("......