Почему java.lang.ThreadLocal является картой в Thread, а не в ThreadLocal?

Наивно я ожидал, что ThreadLocal будет своего рода WeakHashMap Thread для типа значения. Поэтому я был немного озадачен, когда узнал, что значения ThreadLocal на самом деле сохранено на карте в треде. Почему это было сделано именно так? Я ожидаю, что утечек ресурсов, связанных с ThreadLocal, не будет, если значения сохраняются в самом ThreadLocal.

Уточнение: я думал о чем-то вроде

public class AlternativeThreadLocal<T> { 
    private final Map<Thread, T> values = 
        Collections.synchronizedMap(new WeakHashMap<Thread, T>());
    public void set(T value) { values.put(Thread.currentThread(), value); }
    public T get() { return values.get(Thread.currentThread());}    
}

Насколько я вижу, это предотвратило бы странную проблему, заключающуюся в том, что ни ThreadLocal, ни его оставшиеся значения никогда не могут быть собраны мусором до тех пор, пока Thread не умрет, если значение каким-то образом сильно ссылается на сам ThreadLocal. (Возможно, самая коварная форма этого происходит, когда ThreadLocal является статической переменной в классе, на который ссылается значение. Теперь у вас есть большая утечка ресурсов при повторных развертываниях на серверах приложений, поскольку ни объекты, ни их классы не могут быть собраны.)


person Hans-Peter Störr    schedule 01.12.2009    source источник


Ответы (4)


Иногда вы получаете просветление, просто задав вопрос. :-) Теперь я только что увидел один возможный ответ: потокобезопасность. Если карта со значениями находится в объекте Thread, вставка нового значения тривиально поточно-безопасна. Если карта находится в ThreadLocal, у вас возникают обычные проблемы с параллелизмом, которые могут замедлить работу. (Конечно, вы бы использовали ReadWriteLock вместо синхронизации, но проблема остается.)

person Hans-Peter Störr    schedule 01.12.2009

Похоже, вы неправильно понимаете проблему утечек ThreadLocal. Утечки ThreadLocal возникают, когда один и тот же поток используется повторно, например в пуле потоков, и состояние ThreadLocal не очищается между использованиями. Они не являются следствием того, что ThreadLocal остается после уничтожения потока, потому что ничто не ссылается на карту ThreadLocal, кроме самого потока.

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

Как вы сказали в своем ответе, управление параллелизмом упрощается, поскольку карта ThreadLocal представляет собой один экземпляр для каждого потока. Это также делает невозможным для одного потока доступ к локальным объектам другого потока, что может быть не так, если объект ThreadLocal предоставляет API на предложенной вами карте.

person Matt Ryall    schedule 01.12.2009
comment
Более пагубная форма утечек памяти ThreadLocal вызвана замкнутостью между значением и самим ThreadLocal. Это происходит тонкими способами, такими как значение, которое расширяет класс, объявляющий ThreadLocal в качестве члена. - person erickson; 02.12.2009
comment
Я действительно думал о более проблематичной утечке, которую обсуждал сильваркинг. Что касается API: он может быть таким же, как текущий (см. мое пояснение); карта была бы скрыта толковым дизайнером API. :-) - person Hans-Peter Störr; 02.12.2009

Я помню, как несколько лет назад Sun изменила реализацию локальных переменных потока на ее текущую форму. Я не помню, какая это была версия и какой был старый импл.

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

person irreputable    schedule 01.12.2009

Почему Map должен быть на ThreadLocal? Это не имеет особого смысла. Таким образом, это будет карта ThreadLocals для объектов внутри ThreadLocal?

Простая причина, по которой это карта Threads для объектов, заключается в том, что:

  1. Это деталь реализации, т.е. Map никоим образом не подвергается воздействию;
  2. Всегда легко определить текущий поток (с Thread.currentThread()).

Также идея состоит в том, что ThreadLocal может хранить разные значения для каждого потока, который его использует, поэтому имеет смысл, что он основан на потоке, не так ли?

person cletus    schedule 01.12.2009
comment
Смотрите мое уточнение. Я думаю, что реализовать его как члена ThreadLocal гораздо более естественно, но, как я уже говорил в своем ответе, у этого есть недостатки. - person Hans-Peter Störr; 02.12.2009