ThreadLocal浅析

概述

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

常见用法

        ThreadLocal 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  ThreadLocal withInitial(Supplier supplier) {
        return new SuppliedThreadLocal<>(supplier);
    }

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

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

Thread/ThreadLocal/ThreadLocalMap的关系

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

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


    关系图

你可能感兴趣的:(ThreadLocal浅析)