ThreadLocal浅析

概述

ThreadLocal可以看做是线程的局部变量,用于存储线程内部的数据的容器,其内部主要通过ThreadLocalMap类来实现数据的存储与访问。

常见用法

        ThreadLocal<Integer> t = new ThreadLocal<>();
        t.set(1);
        t.get();

源码分析

ThreadLocal相关的源码逻辑比较简单。

ThreadLocalMap

  • ThreadLocalMap内部采用一个继承自WeakReference的Entry数组来存储数据,数组长度为2的幂;
  • 每个Entry存储一个ThreadLocal对象和相应的值;
  • 数据写入:通过ThreadLocal的hash值获取在Entry数组中的位置,若该位置为null,则进行初始化;若该位置已经有值且key不为当前ThreadLocal,则循环向后推移一位,直到数据写入;
  • 数据读取:根据写入的规则,如果该hash位置已经有值且不为当前ThreadLocal,则循环向后推移一位,直到数据写入或读出;
  • 数组扩容等情况暂未研究。

构造方法

        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);
        }

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

getEntry()

       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);
        }

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

remove()

        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

  • ThreadLocal相对比较简单,基本就是从ThreadLocalMap中存取数据;
  • 通过Thread.currentThread()获取当前线程,然后读取当前线程的threadLocals对象,接下来就是ThreadLocalMap的相关操作了;
  • 数据写入:若map不为null,则正常调用map的set(ThreadLocal<?> key, Object value)方法赋值;否则初始化ThreadLocalMap并赋值;
  • 数据读取:若map不为null,则正常调用map的getEntry(ThreadLocal<?> key)方法读取;否则初始化ThreadLocalMap并赋null值,然后返回null;
  • 数据删除:若map不为null,则正常读取;否则不处理;

set()

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

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

withInitial()

    public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
        return new SuppliedThreadLocal<>(supplier);
    }

此方法主要通过Supplier来对ThreadLocal值进行初始化。
用法:

ThreadLocal<Integer> t = ThreadLocal.withInitial(() -> 100);

Thread/ThreadLocal/ThreadLocalMap的关系

简单的归纳了下三者之间的关系:

  • 一个Thread可有多个ThreadLocal;
  • 一个ThreadLocal也可以供多个Thread使用;
  • ThreadLocalMap为Thread的一个全局变量;
  • ThreadLocal为ThreadLocalMap的key;
  • 可以将ThreadLocal作为Thread处理ThreadLocalMap的媒介。


    关系图
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • ThreadLocal用来存储线程隔离的数据。 Thread类中有一个ThreadLocalMap成员变量thre...
    Steven_SHH阅读 1,837评论 0 0
  • 前言 最近在肝原神,略微有些玩物丧志的赶脚。但鲁迅先生说过任何人都逃不过真香定律,即使他再喜欢学习也不行。----...
    小艾咪阅读 1,058评论 0 0
  • 概述 ThreadLocal如果单纯从名字上来看像是“本地线程"这么个意思,只能说这个名字起的确实不太好,很容易让...
    eliter0609阅读 3,395评论 0 0
  • 1、背景 在使用我们的ToolBox(我们公司的数据分析平台) 在往greenplum使用goload 入数据的...
    早点起床晒太阳阅读 1,193评论 0 0
  • 一、ThreadLocal简介及误区 ThreadLocal一般称为线程本地变量,它是一种特殊的线程绑定机制,将变...
    无量散人阅读 2,047评论 0 2

友情链接更多精彩内容