Тест интервью BAT: взаимосвязь между ThreadLocal, ThreadLocalMap и Thread

Java

введение

Из-за большого количества внутренних классовых отношений отношения между этими тремя немного сбивают с толку, когда я впервые читаю это, и мне кажется, что вы внук Лао-цзы и Лао-цзы Лао-цзы. Я по-прежнему рекомендую вам сначала отложить отношения внутренних классов, рассматривать каждый класс как обычный класс, понимать обязанности каждого класса и, наконец, включить внутренний класс, чтобы рассмотреть цель этого дизайна. Вот вам и откровение: столкнувшись со сложными вещами, нужно выскочить, сначала упростить задачу, уловить общее направление, а потом уже дорабатывать каждую функциональную точку и искусство дизайна.

Диаграмма отношений

Далее смотрим на следующую картинку

Из приведенного выше рисунка видно, что Thread содержит ThreadLocalMap, здесь вы можете просто понять, что он содержит массив типа Entry. Ключ Entry имеет тип ThreadLocal, а значение имеет тип Object. То есть ThreadLocalMap может содержать несколько ThreadLocal. Они находятся в отношениях один-ко-многим (разумеется, Entry также задействует технологию слабых ссылок, здесь она не будет развернута, иначе она будет бесконечной)

плюс внутриклассовые отношения

Почему ThreadLocalMap спроектирован как внутренний класс ThreadLocal

Немного головокружительно видеть различные внутренние классы, вы чувствуете, что являетесь внуком Лао-Цзы и Лао-Цзы Лао-Цзы, почему бы не выпустить ThreadLocalMap самостоятельно? Фактически, внутренний класс играет роль инкапсуляции. Приходите, посмотрим на анализ исходного кода

    /**
     * ThreadLocalMap is a customized hash map suitable only for
     * maintaining thread local values. No operations are exported
     * outside of the ThreadLocal class. The class is package private to
     * allow declaration of fields in class Thread.  To help deal with
     * very large and long-lived usages, the hash table entries use
     * WeakReferences for keys. However, since reference queues are not
     * used, stale entries are guaranteed to be removed only when
     * the table starts running out of space.
     */
    static class ThreadLocalMap {
    //这里省略其他属性和方法
    }

В основном это означает, что ThreadLocalMap является локальным для потока значением, и все его методы являются приватными, а это означает, что кроме класса ThreadLocal, другие классы не могут оперировать никакими методами в ThreadLocalMap, так что он может быть прозрачным для других классов. В то же время разрешения этого класса находятся на уровне пакета, что означает, что только классы в рамках одного пакета могут ссылаться на класс ThreadLocalMap, поэтому Thread может ссылаться на ThreadLocalMap, поскольку они находятся в одном пакете.

Хотя Thread может ссылаться на ThreadLocalMap, он не может вызывать какие-либо методы в ThreadLocalMap. Это означает, что мы обычно используем ThreadLocal для получения и установки значений.Посмотрите на следующий код.

public class Test {

    public static void main(String[] args) {
        ThreadLocal<String> local = new ThreadLocal<>();
        local.set("hello word");
        System.out.println(local.get());
    }
}

Но когда мы вызываем метод get класса ThreadLocal, мы фактически получаем значение, вызывая ThreadLdocalMap.

 public T get() {
        //这里通过获取当前的线程
        Thread t = Thread.currentThread();
        //通过线程来获取ThreadLocalMap ,还记得我们上面说的Thread 里面有一个ThreadLocalMap 属性吗?就是这里用上了
        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 getMap(Thread t) {
        return t.threadLocals;
    }

К этому моменту читатели, вероятно, должны понимать, что на самом деле ThreadLdocalMap прозрачен для пользователей и может использоваться как воздух. Мы используем ThreadLocal как можно скорее. Эта конструкция проста при использовании, а инкапсуляция особенная. Хорошо.

Когда ThreadLdocalMap начал привязываться к Thread?

Он привязывается при первом вызове метода set() ThreadLocal Давайте взглянем на исходный код метода set.

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
        //第一次的时候进来这里,因为ThreadLocalMap 还没和Thread 绑定
            createMap(t, value);
    }
    
    //这个时候开始创建一个新的ThreadLocalMap 赋值给Thread 进行绑定
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

прочитать две вещи

Если вы считаете, что этот контент вас очень вдохновляет, я хотел бы пригласить вас сделать мне 2 небольших одолжения:

  1. Ставьте лайк, чтобы больше людей увидело этот контент
  2. Обратите внимание на паблик "Летучая мышь интервью", делитесь оригинальными знаниями время от времени, оригинальность не простая, поддержите пожалуйста побольше (там же есть небольшая программа для расчесывания вопросов).