Все ответы здесь верны, но немного разочаровывают, поскольку они несколько затуманивают умную реализацию ThreadLocal
. Я просто искал исходный код для ThreadLocal
и был приятно впечатлен тем, как он реализован.
Наивная реализация
Если бы я попросил вас реализовать класс ThreadLocal<T>
с учетом API, описанного в javadoc, что бы вы сделали? Начальная реализация, вероятно, будет ConcurrentHashMap<Thread,T>
с использованием Thread.currentThread()
в качестве ключа. Это будет работать достаточно хорошо, но имеет некоторые недостатки.
- Конфликт между потоками -
ConcurrentHashMap
- довольно умный класс, но в конечном итоге ему все равно придется предотвращать какое-либо соединение нескольких потоков с ним, и, если разные потоки будут попадать в него регулярно, будут замедления.
- Постоянно хранит указатель как на нить, так и на объект, даже после того, как нить завершена и может быть обработана GC.
GC-содружественная реализация
Хорошо, попробуйте еще раз, давайте разберемся с проблемой сбора мусора, используя слабые ссылки Работа с WeakReferences может сбить с толку, но этого должно быть достаточно для использования карты, построенной так:
Collections.synchronizedMap(new WeakHashMap<Thread, T>())
Или, если мы используем Гуава (и мы должны быть!):
new MapMaker().weakKeys().makeMap()
Это означает, что когда никто больше не удерживает поток (подразумевая, что он закончен), ключ / значение может быть собран сборщиком мусора, что является улучшением, но все еще не решает проблему конфликта потока, то есть до сих пор ThreadLocal
не все так удивительно в классе. Более того, если кто-то решит удержать Thread
объекты после того, как они закончат, они никогда не будут GC-контролированы, и, следовательно, ни наши объекты, даже если они технически недоступны.
Умная реализация
Мы думали о ThreadLocal
как о сопоставлении потоков со значениями, но, возможно, это не совсем правильный способ думать об этом. Вместо того чтобы думать об этом как о сопоставлении потоков с значениями в каждом объекте ThreadLocal, что, если мы думаем об этом как о сопоставлении объектов ThreadLocal со значениями в каждом потоке ? Если каждый поток хранит сопоставление, а ThreadLocal просто обеспечивает хороший интерфейс для этого сопоставления, мы можем избежать всех проблем предыдущих реализаций.
Реализация будет выглядеть примерно так:
// called for each thread, and updated by the ThreadLocal instance
new WeakHashMap<ThreadLocal,T>()
Здесь нет необходимости беспокоиться о параллелизме, потому что только один поток когда-либо будет обращаться к этой карте.
Разработчики Java имеют здесь большое преимущество перед нами - они могут напрямую разрабатывать класс Thread и добавлять к нему поля и операции, и это именно то, что они сделали.
В java.lang.Thread
есть следующие строки:
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
Что, как следует из комментария, действительно является частно-пакетным отображением всех значений, отслеживаемых ThreadLocal
объектами для этого Thread
. Реализация ThreadLocalMap
- это не WeakHashMap
, но она следует тому же базовому контракту, включая хранение его ключей по слабой ссылке.
ThreadLocal.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();
}
И ThreadLocal.setInitialValue()
вот так:
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
По сути, используйте карту в этой теме для хранения всех наших ThreadLocal
объектов. Таким образом, нам никогда не нужно беспокоиться о значениях в других потоках (ThreadLocal
буквально может получить доступ только к значениям в текущем потоке) и, следовательно, не иметь проблем с параллелизмом. Кроме того, как только Thread
будет сделано, его карта будет автоматически очищена от мусора и все локальные объекты будут очищены. Даже если удерживать Thread
, объекты ThreadLocal
удерживаются по слабой ссылке и могут быть очищены, как только объект ThreadLocal
выходит из области видимости.
Излишне говорить, что эта реализация произвела на меня большое впечатление. Она довольно элегантно решает многие проблемы параллелизма (по общему признанию, используя преимущества ядра Java, но это простительно, поскольку это такой умный класс) и позволяет для быстрого и поточно-ориентированного доступа к объектам, доступ к которым необходим только одному потоку за раз.
tl; dr ThreadLocal
Реализация довольно крутая и намного быстрее / умнее, чем вы думаете на первый взгляд.
Если вам понравился этот ответ, вы также можете оценить мое (менее подробное) обсуждение ThreadLocalRandom
.
Thread
/ ThreadLocal
фрагменты кода, взятые из Реализация Oracle / OpenJDK Java 8 .