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

ThreadLocal源码解析

时间:2024-08-15 15:16:08浏览次数:9  
标签:null tab int len ThreadLocal 源码 value key 解析

ThreadLocal有内部类ThreadLocalMap,ThreadLocalMap是ThreadLocal的核心 1.每个线程下的有一个ThreadLocalMap

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

        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }

    /**
     * The initial capacity -- MUST be a power of two.
     */
    private static final int INITIAL_CAPACITY = 16;

    //底层是个数组,用于存储具体的值
    //Entry的key是ThreadLocal -> weakReference引用
    //Entry的value是具体的值
    private Entry[] table;

    /**
     * The number of entries in the table.
     */
    private int size = 0;

    /**
     * The next size value at which to resize.
     */
    private int threshold; // Default to 0

    /**
     * Set the resize threshold to maintain at worst a 2/3 load factor.
     */
    private void setThreshold(int len) {
        threshold = len * 2 / 3;
    }

    /**
     * Increment i modulo len.
     */
    private static int nextIndex(int i, int len) {
        return ((i + 1 < len) ? i + 1 : 0);
    }

    /**
     * Decrement i modulo len.
     */
    private static int prevIndex(int i, int len) {
        return ((i - 1 >= 0) ? i - 1 : len - 1);
    }
    
    //设置当前线程值
    public void set(T value) {
        //获取当前线程
        Thread t = Thread.currentThread();
        
        //获取当前(Thread)线程下的Map
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            //key.threadLocalHashCode & (len-1); 算出数组下标位置
            //tmp[i] = new Entry(ThreadLocal,value);
            map.set(this, value);
        } else {
            createMap(t, value);
        }
    }
    private Entry getEntry(ThreadLocal<?> key) {
        int i = key.threadLocalHashCode & (table.length - 1);
        Entry e = table[i];
        if (e != null && e.get() == key)
            return e;
        else
            return getEntryAfterMiss(key, i, e);
    }

    /**
     * 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();
    }

    /**
     * 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;
            }
        }
    }

    /**
     * Replace a stale entry encountered during a set operation
     * with an entry for the specified key.  The value passed in
     * the value parameter is stored in the entry, whether or not
     * an entry already exists for the specified key.
     *
     * As a side effect, this method expunges all stale entries in the
     * "run" containing the stale entry.  (A run is a sequence of entries
     * between two null slots.)
     *
     * @param  key the key
     * @param  value the value to be associated with key
     * @param  staleSlot index of the first stale entry encountered while
     *         searching for key.
     */
    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)
                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;

                tab[i] = tab[staleSlot];
                tab[staleSlot] = e;

                // Start expunge at preceding stale entry if it exists
                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.
            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);
    }
}

 
查看以上的代码可以总结出:

1.每个Thread都有一个成员变量

2.设置ThreadLocal.set的时候,会调用Thread t = Thread.currentThread();

3.拿到当前Thread后,就拿当前的Thread成员变量threadLocals

4.然后通过key.threadLocalHashCode & (len-1);。设置[]entry数组下标位置

5.然后通过开放寻址法,进行计算下标位置有冲突则再次计算下标位置,直到不重复。
  系统
  ├── Thread1
  │ ├── threadLocals (ThreadLocalMap 类型)
  │ │ └── Entry[]
  │ │ ├── Entry1
  │ │ │ ├── key: ThreadLocal1 (弱引用)
  │ │ │ └── value: 线程局部变量值1_Thread1
  │ │ ├── Entry2
  │ │ │ ├── key: ThreadLocal2 (弱引用)
  │ │ │ └── value: 线程局部变量值2_Thread1
  │ │ └── ...
  │ └── 其他线程属性...
  ├── Thread2
  │ ├── threadLocals (ThreadLocalMap 类型)
  │ │ └── Entry[]
  │ │ ├── Entry1
  │ │ │ ├── key: ThreadLocal1 (弱引用)
  │ │ │ └── value: 线程局部变量值1_Thread2(可能与Thread1中的值不同)
  │ │ ├── Entry2
  │ │ │ (可能不存在,因为Thread2可能没有使用ThreadLocal2)
  │ │ └── ...
  │ └── 其他线程属性...
  └── ...(其他线程)
   
  ThreadLocal
  ├── set 方法
  │ └── 在当前线程的 ThreadLocalMap 中设置键值对
  └── get 方法
  └── 在当前线程的 ThreadLocalMap 中根据 ThreadLocal 对象作为键查找并返回对应的值

 

static class ThreadLocalMap {    /**     * The entries in this hash map extend WeakReference, using     * its main ref field as the key (which is always a     * ThreadLocal object).  Note that null keys (i.e. entry.get()     * == null) mean that the key is no longer referenced, so the     * entry can be expunged from table.  Such entries are referred to     * as "stale entries" in the code that follows.     */    static class Entry extends WeakReference<ThreadLocal<?>> {        /** The value associated with this ThreadLocal. */        Object value;        Entry(ThreadLocal<?> k, Object v) {            super(k);            value = v;        }    }    /**     * The initial capacity -- MUST be a power of two.     */    private static final int INITIAL_CAPACITY = 16;    /**     * The table, resized as necessary.     * table.length MUST always be a power of two.     */    private Entry[] table;    /**     * The number of entries in the table.     */    private int size = 0;    /**     * The next size value at which to resize.     */    private int threshold; // Default to 0    /**     * Set the resize threshold to maintain at worst a 2/3 load factor.     */    private void setThreshold(int len) {        threshold = len * 2 / 3;    }    /**     * Increment i modulo len.     */    private static int nextIndex(int i, int len) {        return ((i + 1 < len) ? i + 1 : 0);    }    /**     * Decrement i modulo len.     */    private static int prevIndex(int i, int len) {        return ((i - 1 >= 0) ? i - 1 : len - 1);    }    /**     * Construct a new map initially containing (firstKey, firstValue).     * ThreadLocalMaps are constructed lazily, so we only create     * one when we have at least one entry to put in it.     */    ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {        table = new Entry[INITIAL_CAPACITY];        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);        table[i] = new Entry(firstKey, firstValue);        size = 1;        setThreshold(INITIAL_CAPACITY);    }    /**     * Construct a new map including all Inheritable ThreadLocals     * from given parent map. Called only by createInheritedMap.     *     * @param parentMap the map associated with parent thread.     */    private ThreadLocalMap(ThreadLocalMap parentMap) {        Entry[] parentTable = parentMap.table;        int len = parentTable.length;        setThreshold(len);        table = new Entry[len];        for (int j = 0; j < len; j++) {            Entry e = parentTable[j];            if (e != null) {                @SuppressWarnings("unchecked")                ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();                if (key != null) {                    Object value = key.childValue(e.value);                    Entry c = new Entry(key, value);                    int h = key.threadLocalHashCode & (len - 1);                    while (table[h] != null)                        h = nextIndex(h, len);                    table[h] = c;                    size++;                }            }        }    }    /**     * Get the entry associated with key.  This method     * itself handles only the fast path: a direct hit of existing     * key. It otherwise relays to getEntryAfterMiss.  This is     * designed to maximize performance for direct hits, in part     * by making this method readily inlinable.     *     * @param  key the thread local object     * @return the entry associated with key, or null if no such     */    private Entry getEntry(ThreadLocal<?> key) {        int i = key.threadLocalHashCode & (table.length - 1);        Entry e = table[i];        if (e != null && e.get() == key)            return e;        else            return getEntryAfterMiss(key, i, e);    }    /**     * Version of getEntry method for use when key is not found in     * its direct hash slot.     *     * @param  key the thread local object     * @param  i the table index for key's hash code     * @param  e the entry at table[i]     * @return the entry associated with key, or null if no such     */    private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {        Entry[] tab = table;        int len = tab.length;        while (e != null) {            ThreadLocal<?> k = e.get();            if (k == key)                return e;            if (k == null)                expungeStaleEntry(i);            else                i = nextIndex(i, len);            e = tab[i];        }        return null;    }    /**     * 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();    }    /**     * 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;            }        }    }    /**     * Replace a stale entry encountered during a set operation     * with an entry for the specified key.  The value passed in     * the value parameter is stored in the entry, whether or not     * an entry already exists for the specified key.     *     * As a side effect, this method expunges all stale entries in the     * "run" containing the stale entry.  (A run is a sequence of entries     * between two null slots.)     *     * @param  key the key     * @param  value the value to be associated with key     * @param  staleSlot index of the first stale entry encountered while     *         searching for key.     */    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)                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;                tab[i] = tab[staleSlot];                tab[staleSlot] = e;                // Start expunge at preceding stale entry if it exists                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.            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);    }    /**     * Expunge a stale entry by rehashing any possibly colliding entries     * lying between staleSlot and the next null slot.  This also expunges     * any other stale entries encountered before the trailing null.  See     * Knuth, Section 6.4     *     * @param staleSlot index of slot known to have null key     * @return the index of the next null slot after staleSlot     * (all between staleSlot and this slot will have been checked     * for expunging).     */    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;    }    /**     * Heuristically scan some cells looking for stale entries.     * This is invoked when either a new element is added, or     * another stale one has been expunged. It performs a     * logarithmic number of scans, as a balance between no     * scanning (fast but retains garbage) and a number of scans     * proportional to number of elements, that would find all     * garbage but would cause some insertions to take O(n) time.     *     * @param i a position known NOT to hold a stale entry. The     * scan starts at the element after i.     *     * @param n scan control: {@code log2(n)} cells are scanned,     * unless a stale entry is found, in which case     * {@code log2(table.length)-1} additional cells are scanned.     * When called from insertions, this parameter is the number     * of elements, but when from replaceStaleEntry, it is the     * table length. (Note: all this could be changed to be either     * more or less aggressive by weighting n instead of just     * using straight log n. But this version is simple, fast, and     * seems to work well.)     *     * @return true if any stale entries have been removed.     */    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) {                n = len;                removed = true;                i = expungeStaleEntry(i);            }        } while ( (n >>>= 1) != 0);        return removed;    }    /**     * Re-pack and/or re-size the table. First scan the entire     * table removing stale entries. If this doesn't sufficiently     * shrink the size of the table, double the table size.     */    private void rehash() {        expungeStaleEntries();        // Use lower threshold for doubling to avoid hysteresis        if (size >= threshold - threshold / 4)            resize();    }    /**     * 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();                if (k == null) {                    e.value = null; // Help the GC                } 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;    }    /**     * 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);        }    }}
ThreadLocalThreadLocal

标签:null,tab,int,len,ThreadLocal,源码,value,key,解析
From: https://www.cnblogs.com/jichenghui/p/18360986

相关文章

  • Java、python、php版的宠物美容预约服务系统的设计与实现 (源码、调试、LW、开题、PPT)
    ......
  • 本地生活服务平台源码在哪里找到?获取全攻略来了!
    随着本地生活的市场前景和收益空间不断显现,各大官方平台的本地生活服务商申请门槛和审核力度以及入局后的考核要求也在持续调高。在此背景下,越来越多的创业者开始想要通过搭建本地生活服务平台源码完成入局,连带着本地生活服务平台源码在哪里找到等相关问题也因此备受关注。而......
  • 全平台7合一DIY小程序源码系统,一个系统全部搞定 带完整的安装代码包以及搭建部署教程
    系统概述全平台7合一DIY小程序源码系统,是一款集成了目前市场上主流七大平台(包括但不限于微信、支付宝、百度、字节跳动、QQ、京东、快应用)小程序开发能力的综合性源码解决方案。该系统采用模块化设计,支持快速定制与灵活扩展,让企业和开发者无需再为不同平台的小程序开发而烦恼,......
  • 智能名片电子小程序源码系统 附带源码包以及搭建部署教程
    系统概述智能名片电子小程序源码系统,是一款基于最新互联网技术和人工智能算法开发的创新产品。它打破了传统名片的信息传递限制,将个人或企业的基本信息、产品展示、动态更新、在线交流等功能融为一体,通过微信、支付宝等主流平台的小程序形式展现,实现了一站式、多维度的商务信......
  • 基于PHP+MySQL组合开发的同城二手市场便民小程序源码系统 带完整的安装代码包以及搭建
    系统概述随着消费观念的转变和环保意识的增强,越来越多的人开始选择二手商品作为消费首选。然而,传统的二手交易方式往往存在信息不对称、交易效率低下等问题,严重制约了二手市场的发展。因此,开发一款集信息发布、搜索浏览、在线沟通、安全交易于一体的同城二手市场小程序,显得尤......
  • 万能分销商城小程序源码系统 带完整的安装代码包以及搭建部署教程
    系统概述随着移动互联网的普及,小程序成为了人们生活中不可或缺的一部分。企业和商家们逐渐意识到,拥有一个自己的分销商城小程序能够带来巨大的商业价值。然而,从零开始开发一个功能完备的分销商城小程序需要耗费大量的时间、人力和物力。为了解决这一问题,万能分销商城小程序源......
  • 零基础STM32单片机编程入门(三十四) JDY-31蓝牙模块实战含源码
    文章目录一.概要二.JDY-31蓝牙模块主要性能参数三.JDY-31蓝牙模块主芯片BK3432内部框图四.BK3432参考设计五.JDY-31蓝牙模块与单片机通讯方法1.与STM32F103板子硬件连接2.JDY-31蓝牙模块AT指令介绍六.STM32单片机与JDY-31蓝牙模块通讯实验1.硬件准备2.软件工程3.软......
  • 【ArrayList】JDK1.8源码详细注释 以及如何实现线程安全的链表
    ArrayList(JDK8)ArrayList有四个内部类,成员内部类Itr,成员内部类ListItr,静态内部类SubList,ArrayListSpliterator(暂时用不到)Itr是Iterator的实现类,支持正向遍历,ArrayList的iterator方法返回一个Itr对象ListItr是ListIterator的实现类,支持双向遍历,ArrayList的listIterator方法......
  • 探索Gradle:现代化构建工具的深入解析
    目录引言Gradle简介Gradle的安装与配置Gradle的基础概念项目与任务构建脚本依赖管理Gradle构建生命周期初始化阶段配置阶段执行阶段常用Gradle命令Gradle与Maven的对比Gradle插件常见插件自定义插件Gradle的高级特性多项目构建构建缓存持续集成Gradle最佳实践总结......
  • 大模型面试题库精华:100道经典问题解析
    ↓推荐关注↓算法暑期实习机会快结束了,校招大考即将来袭。当前就业环境已不再是那个双向奔赴时代了。求职者在变多,岗位在变少,要求还更高了。最近,我们陆续整理了很多大厂的面试题,帮助网友解惑答疑和职业规划,分享了面试中的那些弯弯绕绕。喜欢本文记得收藏、关注、点赞,更......