【源码-java】java.lang.ThreadLocal 源码阅读

ThreadLocal 的作用是针对目标字段,给每一个线程维护一个独立的变量副本,实现线程隔离,一个Thread中字段的修改不会影响到其他线程。

ThreadLocal是怎样实现线程独立的?

线程独立副本的实现核心依赖于ThreadLocalMap这一数据结构,每一个 Thread 都维护一个 ThreadLocalMap,用于存储该线程的所有 ThreadLocal 变量及其对应的值。下面是 T ThreadLocal<T>.get() 方法的源码:

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
  • 获取当前所在的线程对象,获取到该线程维护的 ThreadLocalMap 对象。
  • ThreadLocalMap 是一个类哈希表数据结构,key是 ThreadLocal 对象,value 是被包装的字段本身;
  • 所以每次调用ThreadLocal.set()时,都是在所在Thread中的ThreadLocalMap中获取到备份字段,修改该备份字段,自然影响不到其他线程。

ThreadLocalMap的数据结构 – 散列表(线性开放寻址)

有一个误区是以为 ThreadLocalMap 底层是一个HashMap

下面是 ThreadLocalMap 的部分源码:

    static 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; // Default to 0
    }

可以看出,Entry 是一个 key-value 结构

  • key:弱引用的ThreadLocal对象,只要该对象没有其他强引用,就会被GC垃圾回收;
  • value:被ThreadLocal包装的T对象

如下图:

用 Entry[] 数组来存储 Entry。接着再来看 ThreadLocalMap.set(ThreadLocal<?> key, Object value)

        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) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

可以看出,这里的哈希表实现 不同于HashMap的处理(发生哈希碰撞存成链表或红黑树),这里用到的是开放寻址法(线性探测)处理hash冲突,也就是当发生hash碰撞时,+1向后寻址,直到找到空位置。

另外,如果 size 达到 threshold,会调用 rehash() 方法进行扩容,重新计算所有键的存储位置,减少冲突。

ThreadLocal的内存泄漏问题

我们知道 ThreadLocalMap 的 key 是弱引用的 ThreadLocal,当该 ThreadLocal 没有强引用时就会被GC垃圾回收掉。然而,value 是强引用的,一旦key被垃圾回收,就会产生一个 key 为 null 的 Entry ,如果不采取措施,这个 Entry 将永远不会被回收,这时就可能发生内存泄漏问题。

因此在使用完ThreadLocal后,一定要记得 new ThreadLocal<>().remove(); 操作!!!

探索散列算法 – 斐波那契(Fibonacci)散列法

// todo:待补充

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇