Интерпретация параллельного программирования Threadlocal

Java задняя часть

Слабый эталонный объект экземпляра ThreadLocal будет сохранен в ThreadLocalMap в качестве клавиши, а затем значение, добавленное методом SET, будет использоваться в качестве значения в ThreadLocalmap. Он предоставляет поточные локальные переменные, чтобы убедиться, что переменные принадлежат к текущему потоку.

Атрибуты

private final int threadLocalHashCode = nextHashCode();

//用于计算threadLocal的hash值,每个对象一直递增
private static AtomicInteger nextHashCode =
    new AtomicInteger();

// 黄金分割数 使散列更加均匀
private static final int HASH_INCREMENT = 0x61c88647;

//通过cas向上累加
private static int nextHashCode() {
    return nextHashCode.getAndAdd(HASH_INCREMENT);
}

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);
}
//获取当前线程中的threadLocalMap
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}
//初始化一个threadLocalMap
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

Ссылка на текущий поток получается с помощью метода Thread.currentThread(), а переменная-член threadLocals экземпляра Thread возвращается непосредственно с помощью getMap. Каждый поток имеет ThreadLocal.ThreadLocalMap, привязанный к ThreadLocal.

ThreadLocalMap

В дополнение к вышеперечисленным атрибутам есть также важный атрибут ThreadLocalMap. ThreadLocalMap — это статический внутренний класс ThreadLocal. Когда поток имеет несколько ThreadLocal, контейнер необходим для управления несколькими ThreadLocals. Функция ThreadLocalMap заключается в управлении несколькими ThreadLocals в исходный код выглядит следующим образом:

static class ThreadLocalMap {
    //键值对的存储结构  继承自弱引用
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;
        // threadLocal为key 被包装成弱引用
        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
}

Threadlocalmap на самом деле представляет собой простую структуру MAP.Внизу массив.У него размер инициализации.Есть также размер порога раскрытия.Элементы массива входные.

set

private void set(ThreadLocal<?> key, Object value) {
    Entry[] tab = table;
    int len = tab.length;
    //计算index
    int i = key.threadLocalHashCode & (len-1);
    //获取对应index的entry 如果不为空
    for (Entry e = tab[i];
         e != null;
         //采用线性探测解决哈希冲突
         e = tab[i = nextIndex(i, len)]) {
        ThreadLocal<?> k = e.get();
        //不为空并且key相等 则替换
        if (k == key) {
            e.value = value;
            return;
        }
        // key为空说明gc掉了 则替换掉改entry
        if (k == null) {
            replaceStaleEntry(key, value, i);
            return;
        }
    }
    //如果为空 则直接新增entry
    tab[i] = new Entry(key, value);
    int sz = ++size;
    //进行启发式的垃圾清理,用于清理无用的Entry
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
}

expungeStaleEntry

private int expungeStaleEntry(int staleSlot) {
    // 1. 将当前的脏entry 置为null,value 置为 null, size,即entry 的数量 减一
    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;
    // 依次循环的使index往后移,直到找到一个 entry = null ,则退出,并返回这个 index
    for (i = nextIndex(staleSlot, len);
         (e = tab[i]) != null;
         i = nextIndex(i, len)) {
        ThreadLocal<?> k = e.get();
        // 在这个过程中,发现脏entry就清除掉,设置为null ,方便GC
        if (k == null) {
            e.value = null;
            tab[i] = null;
            size--;
        } else {
            //这里主要的作用是由于采用了开放地址法,所以删除的元素是多个冲突元素中的一个,需要对后面的元素作
            //处理,可以简单理解就是让后面的元素往前面移动
            //为什么要这样做呢?主要是开放地址寻找元素的时候,遇到null 就停止寻找了,你前面k==null
            //的时候已经设置entry为null了,不移动的话,那么后面的元素就永远访问不了了,下面会画图进行解释说明
            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;
}

МЕТОД ESPUNTESTALEENTRY () состоит в том, чтобы помочь сборщику мусора, в соответствии с исходным кодом, мы можем найти получение и установки методы, скорее всего, будут вызвать методы очистки ExgungeEnseNetry (), поэтому при нормальных обстоятельствах не было бы, но если мы не дойду И установите время из памяти, он, вероятно, столкнется с переполнением памяти, чтобы развить хорошие привычки, когда больше не используют вызов Remove (), чтобы ускорить сбор мусора, чтобы избежать затрагивания памяти.

Утечка памяти ThreadLocal

ThreadLocal будет переработан, когда GC произойдет, когда нет внешней строгой ссылки, тогда значение ключа, сохраненное в ThreadLocalMap, станет нулевым, а на Entry ссылается объект threadLocalMap, а на объект threadLocalMap ссылается объект Thread, а затем, когда Thread не было. Если он будет завершен, объект-значение всегда будет существовать в памяти, что приведет к утечке памяти, поэтому после использования переменной ThreadLocal нам нужно удалить ее вручную.