“ThreadLocal:线程局部变量”的版本间差异

来自Wikioe
跳到导航 跳到搜索
(创建页面,内容为“category:Java == 关于 == <span style="color: blue; font-size: 150%">'''ThreadLocal'''</span> 关键字的概念与作用,在<u>“'''Java 核心技术'''”(卷 I)的“'''并发'''”部分</u><ref name="并发"/>与<u>“'''深入理解 JVM 虚拟机'''”的“'''线程安全'''”部分</u><ref name="线程安全"/>已经了解差不多了。 简单来说:'''ThreadLocal 就是通过定义“线程局部变量”的方式实现“线程…”)
 
第12行: 第12行:
  2、<big>【<span style="color: blue">'''变量传递'''</span>】</big><span style="color: green">为线程开辟独立的存储空间,用于在'''同一线程的流程中保持上下文'''</span>。
  2、<big>【<span style="color: blue">'''变量传递'''</span>】</big><span style="color: green">为线程开辟独立的存储空间,用于在'''同一线程的流程中保持上下文'''</span>。


== 结构关系 ==
== 源码相关 ==
总结:
# <span style="color: blue">'''Thread'''</span> 类:
#* 属性:
#*# '''<syntaxhighlight lang="Java" inline>threadLocals</syntaxhighlight>'''(类型:'''ThreadLocal.ThreadLocalMap''')用于维护“'''线程本地变量'''”。
# <span style="color: blue">'''ThreadLocal'''</span> 类:<span style="color: green">'''当前线程的 ThreadLocalMap 的访问入口'''</span>
#* 属性:
#*# 每个 ThreadLocal 对象都包含了一个独一无二的 '''<syntaxhighlight lang="Java" inline>threadLocalHashCode</syntaxhighlight>''';
#* 方法:
#*# '''<syntaxhighlight lang="Java" inline>set(T value)</syntaxhighlight>''':设置“线程本地变量”的值,将调用 ThreadLocalMap 的 '''<syntaxhighlight lang="Java" inline>set(ThreadLocal<?> key, Object value)</syntaxhighlight>''';
#*# '''<syntaxhighlight lang="Java" inline>T get()</syntaxhighlight>''':获取“线程本地变量”的值,将调用 ThreadLocalMap 的 '''<syntaxhighlight lang="Java" inline>getEntry(ThreadLocal<?> key)</syntaxhighlight>''';
#*# '''<syntaxhighlight lang="Java" inline>remove()</syntaxhighlight>''':删除此“线程本地变量”,将调用 ThreadLocalMap 的 '''<syntaxhighlight lang="Java" inline>remove(ThreadLocal<?> key)</syntaxhighlight>'''<ref neme="Entry&WeakReference"/>;
#**【<span style="color: red">'''泛型类,所以“线程本地变量”的值可以为各种类型'''</span>】
# <span style="color: blue">'''ThreadLocalMap'''</span> 类:<span style="color: green">'''存储了一组“K-V”映射'''(其实质是一个 '''Entry 数组''')</span>
#* '''ThreadLocal''' 的内部类;
#* 属性:
#*# '''<syntaxhighlight lang="Java" inline>private Entry[] table;</syntaxhighlight>''':Entry 数组,用于存储“K-V”映射。【key:“ThreadLocal对象”; value:“线程本地变量的值”】
#* 方法:
#*# '''<syntaxhighlight lang="Java" inline>set(ThreadLocal<?> key, Object value)</syntaxhighlight>''':向 table 中添加/设置 Entry;
#*# '''<syntaxhighlight lang="Java" inline>getEntry(ThreadLocal<?> key)</syntaxhighlight>''':从 table 中获取“ThreadLocal 对象”关联的 Entry;
#*# '''<syntaxhighlight lang="Java" inline>remove(ThreadLocal<?> key)</syntaxhighlight>''':从 table 中移除“ThreadLocal 对象”关联的 Entry;
# <span style="color: blue">'''Entry'''</span> 类:<span style="color: green">'''一个 Entry 就是一个“线程本地变量 -> 线程本地变量的值”的映射'''</span>
#* '''ThreadLocalMap''' 的内部类,继承于:“'''WeakReference'''”<ref neme="Entry&WeakReference"/>;
#* 属性:
#*# '''<syntaxhighlight lang="Java" inline>referent</syntaxhighlight>''':(从 Reference 类中继承而来)保存“ThreadLocal对象”
#*# '''<syntaxhighlight lang="Java" inline>value</syntaxhighlight>''':保存“线程本地变量的值”


=== 源码 ===
# <span style="color: blue">'''Thread'''</span>:
#: <syntaxhighlight lang="Java" highlight="">
package java.lang;


import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import ...


public class Thread implements Runnable {
    ...


    /*
* 与此线程相关的 ThreadLocal 值。此映射由【ThreadLocal 类】维护。
*/
    ThreadLocal.ThreadLocalMap threadLocals = null;


    /*
    * 与此线程相关的 InheritableThreadLocal 值。此映射由【InheritableThreadLocal 类】维护。
    */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
...
}
</syntaxhighlight>
#* '''threadLocals''':保存线程的“线程本地变量”。
#* '''inheritableThreadLocals''':保存线程从父线程继承的“线程本地变量”。
#** '''InheritableThreadLocal 类''':(继承了 ThreadLocal 类)提供从父线程到子线程的值继承:当创建子线程时,子线程接收父线程具有值的所有可继承的“线程本地变量”的初始值。
#*** 通常情况下,孩子的值与父母的值相同;但是,通过重写该类中的“childValue”方法,可以使子级的值成为父级的任意函数。
# <span style="color: blue">'''ThreadLocal'''</span>:
#: <syntaxhighlight lang="Java" highlight="">
package java.lang;
import java.lang.ref.*;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
/**
* 此类提供【“线程局部变量”】。
* 这些变量与它们的正常对应变量不同,因为每个访问一个变量的线程(通过其{@code get}或{@code set}方法)都有自己的、独立初始化的变量副本。
*
* @since  1.2
*/
public class ThreadLocal<T> {
    /**
    * ThreadLocals 依赖于每个线程的 ThreadLocal.ThreadLocalMap(Thread.threadLocals 和 Thread.inheritableThreadLocals)。
* ThreadLocal.ThreadLocalMap 中:
*    ThreadLocal对象充当键,通过 threadLocalHashCode 进行搜索。
*
* 这是一个自定义哈希代码(仅在ThreadLocalMaps中有用):
*    它可以在相同线程使用连续构造的ThreadLocals的常见情况下消除冲突,
*    而在不太常见的情况下保持良好的行为。
    */
    private final int threadLocalHashCode = nextHashCode();
    // 要给出的下一个哈希代码(从零开始,自动更新)
    private static AtomicInteger nextHashCode =
        new AtomicInteger();
    // 连续生成的哈希代码之间的差值
    private static final int HASH_INCREMENT = 0x61c88647;
    /**
    * 返回下一个哈希代码
    */
    private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }
...
    /**
    * 【获取:当前线程的“局部变量”(此 ThreadLocal)】
    */
    public T get() {
// 获取当前线程的 ThreadLocalMap
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
/*
* 如果 ThreadLocalMap 不为空:
*    获取“此 ThreadLocal”关联的 Entry 的值
*/
        if (map != null) {
// 从 ThreadLocalMap 中查找“此 ThreadLocal”关联的 Entry
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
// 获取 Entry 的值(即“此 ThreadLocal”的值)
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
/*
* 如果 ThreadLocalMap 为空:
*    使用“初始值”来为当前线程创建 ThreadLocalMap
*/
        return setInitialValue();
    }
    /**
    * 【设置:当前线程的“局部变量”(此 ThreadLocal)】
    */
    public void set(T value) {
// 获取当前线程的 ThreadLocalMap
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
/*
* 如果“线程的 ThreadLocalMap”不为空:
*    向其中设置 Entry(关联“此 ThreadLocal”、值为 value)
*/
            map.set(this, value);
        else
/*
* 如果 ThreadLocalMap 为空:
*    使用 value 来为当前线程创建 ThreadLocalMap
*/
            createMap(t, value);
    }
    /**
    * 【移除:当前线程的“局部变量”(此 ThreadLocal)】
    */
    public void remove() {
// 获取当前线程的 ThreadLocalMap
        ThreadLocalMap m = getMap(Thread.currentThread());
/*
* 如果 ThreadLocalMap 不为空:
*    移除“此 ThreadLocal”关联的 Entry
*/
        if (m != null)
m.remove(this);
    }
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
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;
    }
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    ...
    /**
    * 内部类:【ThreadLocal.ThreadLocalMap】
*
* ThreadLocalMap是一个定制的哈希映射,仅适用于维护线程本地值。在ThreadLocal类之外不导出任何操作。
* 该类是包私有的,以允许在Thread类中声明字段。
* 为了帮助处理非常大且长期的使用,哈希表条目使用WeakReferences作为键。
* 但是,由于不使用引用队列,只有当表空间开始耗尽时,才保证删除过时的条目。
    */
    static class ThreadLocalMap {
        /**
* 内部类:【ThreadLocal.ThreadLocalMap.Entry】
        * 此哈希映射中的条目扩展 WeakReference,使用其主ref字段作为键(始终是ThreadLocal对象)。
*
* 注意,空键(即entry.get()==null)意味着不再引用该键,所以可以从表中删除该项。
        */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
        // 初始容量 —— 必须是2的幂。
        private static final int INITIAL_CAPACITY = 16;
        // 表,根据需要调整大小。table.length 必须始终是2的幂。
        private Entry[] table;
        // 表中的条目数。
        private int size = 0;
        // 阈值:用于调整大小的下一个size值。
        private int threshold; // Default to 0
        /**
        * 设置调整大小的阈值,以最坏情况下保持2/3的负载系数。
        */
        private void setThreshold(int len) {
            threshold = len * 2 / 3;
        }
...
        /**
        * 构造一个新映射:包含最初的(firstKey,firstValue)。
*
* ThreadLocalMaps是延迟构建的,因此我们只有在至少有一个条目可以放入其中时才创建一个。
        */
        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);
        }
        /**
        * 构造一个新映射:包括给定父映射中的所有可继承ThreadLocal。
*
* 仅由createInheritedMap调用。
        */
        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++;
                    }
                }
            }
        }
        /**
        * 【获取与 ThreadLocal 关联的 Entry】
*
* 此方法本身仅处理快速路径:
*    直接命中现有键。否则它将中继到 getEntryAfterMiss。
*    这是为了最大限度地提高直接命中的性能,部分原因是使该方法易于内联。
        */
        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);
        }
        /**
        * 在其直接哈希槽中找不到键时使用的 getEntry 方法的版本。
        */
        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;
        }
        /**
        * 【添加与 ThreadLocal 关联的 Entry】
        */
        private void set(ThreadLocal<?> key, Object value) {
            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) {
// 将 set 操作期间遇到的过时项替换为指定 key 的项
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
            tab[i] = new Entry(key, value);
            int sz = ++size;
// 试探性地扫描一些单元格以查找过时的条目。
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
// 重新包装 和/或 调整 table 大小。
                rehash();
        }
        /**
        * 【移除与 ThreadLocal 关联的 Entry】
        */
        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;
                }
            }
        }
        ...
    }
}
</syntaxhighlight>
#* ThreadLocal 实例通常是希望将状态与线程关联的类中的“私有静态”('''private static''')字段(例如,用户ID 或 事务ID):
#*: <syntaxhighlight lang="Java" highlight="">
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadId {
    // Atomic integer containing the next thread ID to be assigned
    private static final AtomicInteger nextId = new AtomicInteger(0);
    // Thread local variable containing each thread's ID
    private static final ThreadLocal<Integer> threadId =
        new ThreadLocal<Integer>() {
            @Override
            protected Integer initialValue() {
                return nextId.getAndIncrement();
        }
    };
    // Returns the current thread's unique ID, assigning it if necessary
    public static int get() {
        return threadId.get();
    }
}
</syntaxhighlight>
#*: 如上,类生成每个线程本地的唯一标识符。线程的id在第一次调用{@code ThreadId.get()}时被分配,并且在后续调用时保持不变。
#* 只要线程处于活动状态且 ThreadLocal 实例可访问,每个线程都保持对其线程本地变量副本的隐式引用;当一个线程离开后,它的所有线程本地实例副本都将被垃圾收集(除非存在对这些副本的其他引用)。


== 应用过程 ==
== 应用过程 ==

2023年1月1日 (日) 03:06的版本


关于

ThreadLocal 关键字的概念与作用,在Java 核心技术”(卷 I)的“并发”部分[1]深入理解 JVM 虚拟机”的“线程安全”部分[2]已经了解差不多了。


简单来说:ThreadLocal 就是通过定义“线程局部变量”的方式实现“线程本地存储”。


其作用(或者说应用场景)[3]:
1、变量隔离为变量在每个线程中创建一个副本,避免同一参数在所有线程中传递。
2、变量传递为线程开辟独立的存储空间,用于在同一线程的流程中保持上下文

源码相关

总结:

  1. Thread 类:
    • 属性:
      1. threadLocals(类型:ThreadLocal.ThreadLocalMap)用于维护“线程本地变量”。
  2. ThreadLocal 类:当前线程的 ThreadLocalMap 的访问入口
    • 属性:
      1. 每个 ThreadLocal 对象都包含了一个独一无二的 threadLocalHashCode
    • 方法:
      1. set(T value):设置“线程本地变量”的值,将调用 ThreadLocalMap 的 set(ThreadLocal<?> key, Object value)
      2. T get():获取“线程本地变量”的值,将调用 ThreadLocalMap 的 getEntry(ThreadLocal<?> key)
      3. remove():删除此“线程本地变量”,将调用 ThreadLocalMap 的 remove(ThreadLocal<?> key)引用错误:无效<ref>标签;无效名称,可能是过多
      • 泛型类,所以“线程本地变量”的值可以为各种类型
  3. ThreadLocalMap 类:存储了一组“K-V”映射(其实质是一个 Entry 数组
    • ThreadLocal 的内部类;
    • 属性:
      1. private Entry[] table;:Entry 数组,用于存储“K-V”映射。【key:“ThreadLocal对象”; value:“线程本地变量的值”】
    • 方法:
      1. set(ThreadLocal<?> key, Object value):向 table 中添加/设置 Entry;
      2. getEntry(ThreadLocal<?> key):从 table 中获取“ThreadLocal 对象”关联的 Entry;
      3. remove(ThreadLocal<?> key):从 table 中移除“ThreadLocal 对象”关联的 Entry;
  4. Entry 类:一个 Entry 就是一个“线程本地变量 -> 线程本地变量的值”的映射
    • ThreadLocalMap 的内部类,继承于:“WeakReference引用错误:无效<ref>标签;无效名称,可能是过多
    • 属性:
      1. referent:(从 Reference 类中继承而来)保存“ThreadLocal对象”
      2. value:保存“线程本地变量的值”

源码

  1. Thread
    package java.lang;
    
    import java.lang.ref.Reference;
    import java.lang.ref.ReferenceQueue;
    import java.lang.ref.WeakReference;
    import ...
    
    public class Thread implements Runnable {
        ...
    
        /* 
    	 * 与此线程相关的 ThreadLocal 值。此映射由【ThreadLocal 类】维护。
    	 */
        ThreadLocal.ThreadLocalMap threadLocals = null;
    
        /*
         * 与此线程相关的 InheritableThreadLocal 值。此映射由【InheritableThreadLocal 类】维护。
         */
        ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    	
    	...
    }
    
    • threadLocals:保存线程的“线程本地变量”。
    • inheritableThreadLocals:保存线程从父线程继承的“线程本地变量”。
      • InheritableThreadLocal 类:(继承了 ThreadLocal 类)提供从父线程到子线程的值继承:当创建子线程时,子线程接收父线程具有值的所有可继承的“线程本地变量”的初始值。
        • 通常情况下,孩子的值与父母的值相同;但是,通过重写该类中的“childValue”方法,可以使子级的值成为父级的任意函数。
  2. ThreadLocal
    package java.lang;
    import java.lang.ref.*;
    import java.util.Objects;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.function.Supplier;
    
    /**
     * 此类提供【“线程局部变量”】。
     * 这些变量与它们的正常对应变量不同,因为每个访问一个变量的线程(通过其{@code get}或{@code set}方法)都有自己的、独立初始化的变量副本。
     * 
     * @since   1.2
     */
    public class ThreadLocal<T> {
        /**
         * ThreadLocals 依赖于每个线程的 ThreadLocal.ThreadLocalMap(Thread.threadLocals 和 Thread.inheritableThreadLocals)。
    	 * ThreadLocal.ThreadLocalMap 中:
    	 *     ThreadLocal对象充当键,通过 threadLocalHashCode 进行搜索。
    	 *
    	 * 这是一个自定义哈希代码(仅在ThreadLocalMaps中有用):
    	 *     它可以在相同线程使用连续构造的ThreadLocals的常见情况下消除冲突,
    	 *     而在不太常见的情况下保持良好的行为。
         */
        private final int threadLocalHashCode = nextHashCode();
    
        // 要给出的下一个哈希代码(从零开始,自动更新)
        private static AtomicInteger nextHashCode =
            new AtomicInteger();
    
        // 连续生成的哈希代码之间的差值
        private static final int HASH_INCREMENT = 0x61c88647;
    
        /**
         * 返回下一个哈希代码
         */
        private static int nextHashCode() {
            return nextHashCode.getAndAdd(HASH_INCREMENT);
        }
    
    	...
    
        /**
         * 【获取:当前线程的“局部变量”(此 ThreadLocal)】
         */
        public T get() {
    		// 获取当前线程的 ThreadLocalMap
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
    		/*
    		 * 如果 ThreadLocalMap 不为空:
    		 *     获取“此 ThreadLocal”关联的 Entry 的值
    		 */
            if (map != null) {
    			// 从 ThreadLocalMap 中查找“此 ThreadLocal”关联的 Entry
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
    				// 获取 Entry 的值(即“此 ThreadLocal”的值)
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
    		/*
    		 * 如果 ThreadLocalMap 为空:
    		 *     使用“初始值”来为当前线程创建 ThreadLocalMap
    		 */
            return setInitialValue();
        }
    
        /**
         * 【设置:当前线程的“局部变量”(此 ThreadLocal)】
         */
        public void set(T value) {
    		// 获取当前线程的 ThreadLocalMap
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
    
            if (map != null)
    			/*
    			 * 如果“线程的 ThreadLocalMap”不为空:
    			 *     向其中设置 Entry(关联“此 ThreadLocal”、值为 value)
    			 */
                map.set(this, value);
            else
    			/*
    			 * 如果 ThreadLocalMap 为空:
    			 *     使用 value 来为当前线程创建 ThreadLocalMap
    			 */
                createMap(t, value);
        }
    
        /**
         * 【移除:当前线程的“局部变量”(此 ThreadLocal)】
         */
        public void remove() {
    		// 获取当前线程的 ThreadLocalMap
            ThreadLocalMap m = getMap(Thread.currentThread());
    		/*
    		 * 如果 ThreadLocalMap 不为空:
    		 *     移除“此 ThreadLocal”关联的 Entry
    		 */
            if (m != null)
    			m.remove(this);
        }
    
        ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }
    	
    	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;
        }
    
        void createMap(Thread t, T firstValue) {
            t.threadLocals = new ThreadLocalMap(this, firstValue);
        }
    
        ...
    
        /**
         * 内部类:【ThreadLocal.ThreadLocalMap】
    	 * 
    	 * ThreadLocalMap是一个定制的哈希映射,仅适用于维护线程本地值。在ThreadLocal类之外不导出任何操作。
    	 * 该类是包私有的,以允许在Thread类中声明字段。
    	 * 为了帮助处理非常大且长期的使用,哈希表条目使用WeakReferences作为键。
    	 * 但是,由于不使用引用队列,只有当表空间开始耗尽时,才保证删除过时的条目。
         */
        static class ThreadLocalMap {
    
            /**
    		 * 内部类:【ThreadLocal.ThreadLocalMap.Entry】
             * 此哈希映射中的条目扩展 WeakReference,使用其主ref字段作为键(始终是ThreadLocal对象)。
    		 * 
    		 * 注意,空键(即entry.get()==null)意味着不再引用该键,所以可以从表中删除该项。
             */
            static class Entry extends WeakReference<ThreadLocal<?>> {
                /** The value associated with this ThreadLocal. */
                Object value;
    
                Entry(ThreadLocal<?> k, Object v) {
                    super(k);
                    value = v;
                }
            }
    
            // 初始容量 —— 必须是2的幂。
            private static final int INITIAL_CAPACITY = 16;
    
            // 表,根据需要调整大小。table.length 必须始终是2的幂。
            private Entry[] table;
    
            // 表中的条目数。
            private int size = 0;
    
            // 阈值:用于调整大小的下一个size值。
            private int threshold; // Default to 0
    
            /**
             * 设置调整大小的阈值,以最坏情况下保持2/3的负载系数。
             */
            private void setThreshold(int len) {
                threshold = len * 2 / 3;
            }
    
    		...
    
            /**
             * 构造一个新映射:包含最初的(firstKey,firstValue)。
    		 * 
    		 * ThreadLocalMaps是延迟构建的,因此我们只有在至少有一个条目可以放入其中时才创建一个。
             */
            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);
            }
    
            /**
             * 构造一个新映射:包括给定父映射中的所有可继承ThreadLocal。
    		 * 
    		 * 仅由createInheritedMap调用。
             */
            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++;
                        }
                    }
                }
            }
    
            /**
             * 【获取与 ThreadLocal 关联的 Entry】
    		 * 
    		 * 此方法本身仅处理快速路径:
    		 *     直接命中现有键。否则它将中继到 getEntryAfterMiss。
    		 *     这是为了最大限度地提高直接命中的性能,部分原因是使该方法易于内联。
             */
            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);
            }
    
            /**
             * 在其直接哈希槽中找不到键时使用的 getEntry 方法的版本。
             */
            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;
            }
    
            /**
             * 【添加与 ThreadLocal 关联的 Entry】
             */
            private void set(ThreadLocal<?> key, Object value) {
                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) {
    					// 将 set 操作期间遇到的过时项替换为指定 key 的项
                        replaceStaleEntry(key, value, i);
                        return;
                    }
                }
    
                tab[i] = new Entry(key, value);
                int sz = ++size;
    			// 试探性地扫描一些单元格以查找过时的条目。
                if (!cleanSomeSlots(i, sz) && sz >= threshold)
    				// 重新包装 和/或 调整 table 大小。
                    rehash();
            }
    
            /**
             * 【移除与 ThreadLocal 关联的 Entry】
             */
            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 实例通常是希望将状态与线程关联的类中的“私有静态”(private static)字段(例如,用户ID 或 事务ID):
      import java.util.concurrent.atomic.AtomicInteger;
      
      public class ThreadId {
          // Atomic integer containing the next thread ID to be assigned
          private static final AtomicInteger nextId = new AtomicInteger(0);
      
          // Thread local variable containing each thread's ID
          private static final ThreadLocal<Integer> threadId =
              new ThreadLocal<Integer>() {
                  @Override 
                  protected Integer initialValue() {
                      return nextId.getAndIncrement();
              }
          };
      
          // Returns the current thread's unique ID, assigning it if necessary
          public static int get() {
              return threadId.get();
          }
      }
      
      如上,类生成每个线程本地的唯一标识符。线程的id在第一次调用{@code ThreadId.get()}时被分配,并且在后续调用时保持不变。
    • 只要线程处于活动状态且 ThreadLocal 实例可访问,每个线程都保持对其线程本地变量副本的隐式引用;当一个线程离开后,它的所有线程本地实例副本都将被垃圾收集(除非存在对这些副本的其他引用)。

应用过程

参考

  1. 参考:【核心技术:并发#线程局部变量(ThreadLocal)
  2. 参考:【深入理解JVM:线程安全与锁优化#无同步方案
  3. 关于 ThreadLocal 的作用:
    1. 【变量隔离】:就是用于实现“线程安全”。
    2. 【变量传递】:参考【Hibernate笔记_4:核心知识#关于“Session_管理方式:绑定到本地线程”】(将业务层获取的 Session 传递到 DAO 层)。