1 тема закрыта
Когда несколько потоков обращаются к общим переменным данным, возникает проблема синхронизации данных между потоками. Не всегда используются общие данные, поэтому предлагается концепция закрытия потока.
Данные заключены в свой собственный поток, поэтому нет необходимости в синхронизации.Этот метод предотвращения использования синхронизации путем включения данных в поток называетсязакрытие потока.
Самый простой способ избежать параллелизма — закрытие потока.То есть, если объект инкапсулирован в поток, только поток может видеть объект, тогда даже если объект не является потокобезопасным, проблем с безопасностью параллелизма не возникнет.
1.1 Закрытие стека
Одним из неотъемлемых свойств локальных переменных является то, что они заключены в поток. Они находятся в стеке исполняющего потока, к которому не могут получить доступ другие потоки.
1.2 ИспользованиеThreadLocal
является лучшей практикой для реализации сдерживания потоков.
ThreadLocal — это переменная особого типа в Java.Это переменная уровня потока, у каждого потока есть ThreadLocal, то есть у каждого потока есть своя независимая переменная, состояние гонки полностью устранено, и это абсолютно безопасная переменная в concurrent режиме.
- Применение
-
ThreadLocal<T> var = new ThreadLocal<T>();
Копия T автоматически создается в каждом потоке, и копии не зависят друг от друга и не влияют друг на друга. Вы можете использовать ThreadLocal для хранения некоторых параметров для использования в нескольких методах потока вместо передачи параметров.
примерThreadLocal
Карта поддерживается внутри, ключ карты — это имя каждого потока, а значение карты — это объект, который мы хотим закрыть Объект в каждом потоке соответствует значению в карте, то естьThreadLocal
Закрытие потока объекта реализуется с помощью Map.
Для игр CS в начале каждый может получить пистолет, а на рукоятке пистолета есть три цифры: количество пуль, количество убийств и количество жизней, для него установлены начальные значения 1500 , 0 и 10 соответственно.
Если предположить, что все на поле боя - нить, то куда записываются эти три начальных значения? Если каждый поток запишет эти три значения насмерть, что если начальное количество пуль равномерно изменить на 1000 патронов? Если разделить, то одновременные изменения между потоками приведут к неточным данным. Можете ли вы создать такой объект, установить этот объект как общую переменную и установить начальное значение единообразно, но каждый поток изменяет это значение независимо друг от друга. Этот объект — ThreadLocal
Обратите внимание, что его нельзя преобразовать в локальный или собственный поток.
Правильное имя на английском должно называться: CopyValueIntoEveryThread
образец кода
Это действительно трудно понять. Можно понять, что JVM поддерживает карту. Когда каждый поток использует этот T, он использует текущий поток, чтобы получить его из карты. понимается только как понятие
В этом примере нет операции установки, так как же начальное значение входит в каждый поток и становится независимой копией?ThreadLocal
Переопределено в определенииinitialValue()
, но не вBULLET_ NUMBER_ THREADLOCAL
Выполняется, когда объект загружает статические переменные; вместо этого каждый потокThreadLocal.get()
будет выполнен, когда; его исходный код выглядит следующим образом
У каждой нити свояThreadLocalMap
;еслиmap ==null
, затем выполнить напрямуюsetInitialValue()
; Если карта создана, значит класс ThreadthreadLocals
свойство было инициализировано; еслиe==null
, будет выполняться до тех пор, покаsetinitialValue()
setinitialValue()
Исходный код выглядит следующим образом:Это защищенный метод, значение = initialValue() было перезаписано при инициализации объекта ThreadLocal в CsGameByThreadLocal; getMap
Исходный код предназначен для извлечения свойства ThreadLocalMap объекта потока t: t.threadLocals.
существует
CsGameByThreadLocal
1 место, используяThreadLocalRandom
создать отдельныйRandom
пример;
Этот класс был введен в JDK7, он позволяет каждому потоку иметь собственный генератор случайных чисел;
мы хотим избежатьRandom
Экземпляр используется несколькими потоками, хотя совместное использование экземпляра является потокобезопасным.seed
что приводит к снижению производительности.
мы уже знаемThreadLocal
Он хранится каждым потоком отдельно; поскольку у каждого потока есть независимая копия переменной, другие потоки не могут получить к ней доступ, поэтому нет проблем с безопасностью потоков, и это не повлияет на производительность выполнения программы.ThreadLocal
Объекты обычноprivate static
Изменено, потому что их все нужно копировать в локальный поток, поэтому не-static
оказывает незначительное влияние, однакоThreadLocal
Не удалось решить проблему обновления общих объектов, это продемонстрирует следующий пример.CsGameByThreadLocal
используется вInteger
Неизменяемые объекты, поэтому вы можете использовать одну и ту же кодировку для управления изменяемыми объектами, чтобы увидетьРезультаты вывода неупорядочены и неконтролируемы, поэтому при использовании ссылки для работы с общим объектом по-прежнему требуется синхронизация потоков.
ThreadLocal
статический внутренний классThreadLocalMap
, который также имеет статический внутренний классEntry
; в потокеThreadLocalMap
Назначение свойств находится вThreadLocal
в классеcreateMap
.
ThreadLocal
а такжеThreadLocalMap
Есть три группы соответствующих методов: get(), set() и remove(); вThreadLocal
По ним выносятся только суждения по контрольной сумме, а окончательная реализация попадет вThreadLocalMap.
.Entry
унаследовано отWeakReference
, есть только одна переменная-член значения, ее ключом является объект ThreadLocal
Давайте посмотрим на взаимосвязь между стеком и кучей памяти с точки зрения двухПоток имеет один и только одинThreadLocalMap
объект одинEntry
Ключевые слабые ориентиры объекта указывают наThreadLocal
объект одинThreadLocalMap
Объект хранит несколько объектов Entry одинThreadLocal
Объекты могут совместно использоваться несколькими потокамиThreadLocal
Объект не содержит значение, значение хранится в объекте Entry потока.
Исходный код объекта Entry выглядит следующим образом
всеEntry
объектыThreadLocalMap
объект экземпляра классаthreadLocals
Hold; когда поток выполняется, атрибуты экземпляра в потоке будут удалены сборщиком мусора, а слабоссылочные атрибутыThreadLocal
, даже если поток выполняется, покаThreadLocal
Ссылка на объект устанавливается наnull
,Entry
Ключ будет автоматически удален сборщиком мусора на следующем Y-GC;ThreadLocal
использоватьset()/get()
, он автоматически преобразует этиkey=null
Значение установлено наnull
, чтобы значение можно было GCed, чтобы избежать утечек памяти, реальность очень тощая, ThreadLocal, как описано в комментариях к исходному коду:ThreadLocal
Объекты обычно используются как приватные статические переменные, поэтому их жизненный цикл не закончится как минимум с окончанием потока.
Три важных метода:
- set()
Если нет установленной операцииThreadLocal
, легко вызвать проблемы с грязными данными - get()
Всегда нет операции полученияThreadLocal
объекты бессмысленны - remove()
Если нет операции удаления, легко вызвать утечку памяти.
еслиThreadLocal
Он нестатичен и принадлежит экземпляру потока, тогда он теряет существенное свойство совместного использования потоков; тогдаThreadLocal
Что он делает? Мы знаем, что локальные переменные передаются между различными блоками кода в методе, а переменные класса передаются между методами в классе; сложные методы потока могут потребовать вызова многих методов для достижения определенной функции. -thread переменные?ThreadLocal
, он обычно используется в одном потоке для передачи данных между классами и методами, если нет ThreadLocal, то передача информации между собой обречена опираться на возвращаемые значения и параметры, так что незаметно некоторые классы и даже некоторые фреймворки будут взаимодействовать друг с другом Coupling; установив для последнего параметра конструктора Thread значение true, переменные текущего потока могут продолжать передаваться в дочерний поток, который он создает
public Thread (ThreadGroup group, Runnable target, String name,long stackSize, boolean inheritThreadLocals) [
this (group, target, name, stackSize, null, inheritThreadLocals) ;
}
parent является его родительским потоком
if (inheritThreadLocals && parent. inheritableThreadLocals != null)
this. inheritableThreadLocals = ThreadLocal. createInheritedMap (parent. inheritableThreadLocals) ;
createlnheritedMap()
На самом деле, вызываяThreadLocalMap
Частный конструктор для создания экземпляра объекта, помещаемый в родительский потокnull
Переменные потока копируются
private ThreadLocalMap (ThreadLocalMap parentMap) {
// table就是存储
Entry[] parentTable = parentMap. table;
int len = parentTable. length;
setThreshold(len) ;
table = new Entry[len];
for (Entry e : parentTable) {
if (e != null) {
ThreadLocal<object> key = (ThreadLocal<object>) e.get() ;
if (key != null) {
object value = key. childValue(e.value) ;
Entry c = new Entry(key, value) ;
int h = key. threadLocalHashCode & (len - 1) ;
while (table[h] != null)
h = nextIndex(h, len) ;
table[h] = C;
size++;
}
}
}
Можно пройти во многих сценарияхThreadLocal
Для прозрачной передачи глобального контекста, например, используйтеThreadLocal
Для хранения определенного маркерного бита системы мониторинга он временно называется traceld.Все traceld по определенному запросу согласованы для получения лог-файлов, которые можно анализировать единообразно, но в фактическом процессе разработки обнаруживается, что traceld в дочерний поток имеет значение null, что не согласуется с traceld основного потока, поэтому для этого требуется то, что я только что сказал.InheritableThreadLocal
Чтобы решить проблему совместного использования переменных потока между родительским и дочерним потоками, сделайте traceld согласованным на протяжении всего процесса соединения.Пример кода выглядит следующим образом.
import org.apache.commons.lang3.StringUtils;
/**
* @author sss
* @date 2019/1/17
*/
public class RequestProcessTrace {
private static final InheritableThreadLocal<FullLinkContext> FULL_LINK_CONTEXT_INHERITABLE_THREAD_LOCAL
= new InheritableThreadLocal<FullLinkContext>();
public static FullLinkContext getContext() {
FullLinkContext fullLinkContext = FULL_LINK_CONTEXT_INHERITABLE_THREAD_LOCAL.get();
if (fullLinkContext == null) {
FULL_LINK_CONTEXT_INHERITABLE_THREAD_LOCAL.set(new FullLinkContext());
fullLinkContext = FULL_LINK_CONTEXT_INHERITABLE_THREAD_LOCAL.get();
}
return fullLinkContext;
}
private static class FullLinkContext {
private String traceId;
public String getTraceId() {
if (StringUtils.isEmpty(traceId)) {
FrameWork.startTrace(null, "JavaEdge");
traceId = FrameWork.getTraceId();
}
return traceId;
}
public void setTraceId(String traceId) {
this.traceId = traceId;
}
}
}
использоватьThreadLocal
а такжеInheritableThreadLocal
Когда контекст передается прозрачно, необходимо обратить внимание на обработку переключения между потоками и аномальной передачи, чтобы избежать потери контекста из-за неправильной обработки в процессе передачи.
наконец,SimpleDateFormat
Это не потокобезопасный класс, определенный как статический, и есть риск синхронизации данных, это видно из исходного кода,SimpleDateFormat
есть один внутриCalendar
Object; в процессе преобразования даты в строку или строки в дату вероятны ошибки при совместном использовании несколькими потоками; рекомендуется использовать ThreadLocal
, пусть каждый поток владеет этим объектом индивидуально.
Побочные эффекты ThreadLocal
Чтобы совместно использовать переменную потокобезопасно, JDK предоставляетThreadLocal
.ноThreadLocal
Основная проблема заключается в генерации грязных данных и утечке памяти; эти две проблемы обычно вызваны использованием ThreadLocal в потоках пула потоков, поскольку пул потоков имеет повторное использование потоков и резидентность памяти. потоки пула потоков. Вызвано тем, что пул потоков имеет характеристики повторного использования потоков и резидентной памяти.
1 грязные данные
Повторное использование потоков будет генерировать грязные данные, потому что пул потоков будет использоваться повторно.Thread
объект, сThread
Привязка статических свойствThreadLoca
l переменные также используются повторно, если они не вызываются явно в реализованном методе run() потока.remove()
Очистить тему, связанную сThreadLocal
информации, то если следующий поток не вызоветset()
, возможноget()
повторно использовать информацию о потоке, в том числеThreadLocal
связанного объекта потокаvalueстоимость.
Проблемы с грязным чтением на самом деле очень распространены. Например, пользователь А не видит запись заказа после размещения заказа, но пользователь Б видит запись заказа пользователя А. В ходе расследования было установлено, что это вызвано оптимизацией сеанса. процесс запроса, пользователь каждый раз Для каждого запроса к Серверу необходимо запрашивать информацию о сеансе пользователя в кеше через sessionId, что, несомненно, увеличивает вызов.Поэтому инженер решил использовать фреймворк для кэширования соответствующих данных каждый пользователь.SecurityContext
, который инкапсулирует информацию, связанную с сеансом. Хотя после оптимизации для каждого пользователя будет создан новый контекст, связанный с сеансом, из-заThreadlocal
несвоевременно в конце обработки потокаremove()
; В сценариях с высокой степенью параллелизма потоки в пуле потоков могут считывать информацию о пользователе, кэшированную предыдущим потоком.
- образец кода
2 утечки памяти
Предлагать использовать статическое ключевое слово для изменения в комментариях к исходному кодуThreadLocal
, В этом сценарии надеждаThreadLocal
После того, как объект теряет свою ссылку, запускается механизм слабых ссылок для повторного использования.Entry
изValue
нереалистично. В приведенном выше примере, если вы неremove()
, затем, когда поток завершит выполнение, передайтеThreadLocal
Объект String, удерживаемый объектом, не будет освобожден.
- **Решение двух вышеуказанных проблем очень простое.
Каждый раз, когда у вас заканчивается ThreadLocal, вызывайте его вовремяremove()
убирать**
What is ThreadLocal
Этот класс предоставляет локальные переменные потока; они отличаются от своих обычных аналогов, потому что каждый поток, обращающийся к переменной (через свои методы get/set), имеет свою собственную локальную переменную, которая не зависит от переменной, являющейся инициализированной копией .
ThreadLocal
Экземпляры обычно находятся внутри классаprivate static
поле, хотите связать состояние с определенным потоком (например, идентификатор пользователя или идентификатор транзакции).
один сThreadLocal
Структура хранения с объектами в качестве ключей и произвольными объектами в качестве значений; что-то вродеHashMap
, может сохранять пары ключ-значение "ключ : значение", ноThreadLocal
Можно сохранить только одну пару ключ-значение, и данные каждого потока не мешают друг другу Структура привязана к потоку, что означает, что поток можетThreadLocal
Объект запрашивается для значения, привязанного к этому потоку.
ThreadLocal<String> localName = new ThreadLocal();
localName.set("JavaEdge");
String name = localName.get();
В потоке A инициализируется объект ThreadLocal localName и устанавливается значение JavaEdge, при этом ранее установленное значение можно получить через get в потоке A, но если оно находится в потоке B, то оно будет null.
потому чтоThreadLocal
Это гарантирует, что данные каждого потока не мешают друг другу.Посмотрите на исходный код методов set(T value) и get()
Видимый, каждый поток имеетThreadLocalMap
- При выполнении set его значение сохраняется в текущем потоке
threadLocals
Переменная - При выполнении get из текущего потока
threadLocals
переменное приобретение
Поэтому значение, установленное в потоке А, никогда не будет получено потоком В. Даже если оно будет сброшено в потоке В, это не повлияет на значение в А, это гарантирует, что потоки не будут мешать друг другу.
В поисках сущности - Структура
Судя по названию, он похож на HashMap, но вThreadLocal
, не реализует интерфейс Map
- существует
ThreadLoalMap
, также инициализируйте массив Entry размером 16
- Объект узла Entry используется для хранения каждой пары ключ-значение.
здесьключ всегда ThreadLocal;
пройти черезThreadLocal
изset()
,ПучокThreadLocal
В качестве ключа используется сам объект, вставленный вThreadLoalMap
ThreadLoalMap
изEntry
наследоватьWeakReference
Сильно отличается от HashMap,Entry
Нет вnext
поле, поэтому ситуация со связанным списком отсутствует.
хэш-коллизия
Что происходит при конфликте хэшей без связанного списка?
Первый взглядThreadLoalMap
Вставьте реализацию ключа/значения
- каждый
ThreadLocal
Объекты имеют хеш-значение —threadLocalHashCode
- каждый инициализированный
ThreadLocal
объект, хеш-значение увеличивается на фиксированный размер
Во время введения, согласноThreadLocal
Хэш-значение объекта находится в позиции i в таблице Процесс выглядит следующим образом
- Если текущая позиция пуста, инициализируйте объект Entry значением i;
- Есть объект в позиции i
- если
Entry
Ключ объекта — это ключ, который нужно установить, перезаписывая его значение (так же, как HashMap); - Если это не связано с устанавливаемым ключом, найдите следующую вакансию
- если
Итак, вget
, также согласноThreadLocal
Хеш-значение объекта находится в позиции в таблице, затем оценивается, согласуется ли ключ в объекте Entry в этой позиции с ключом get, и если не соответствует, оценивается следующая позиция.
Видно, что если конфликт между set и get серьезный, эффективность очень низкая, т.к.ThreadLoalMap
Это атрибут Thread, поэтому, даже если вы контролируете количество элементов, установленных в вашем собственном коде, вы все равно не можете контролировать поведение других кодов.
утечка памяти
ThreadLocal может вызвать утечку памяти, почему? Первый взгляд на реализацию Entry:
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
Из предыдущего анализа было известно, что при использовании ThreadLocal для сохранения значения объект Entry будет вставлен в массив в ThreadLocalMap.Разумеется, что ключ-значение должно храниться в объекте Entry со строгой ссылкой, но в реализации ThreadLocalMap ключ сохраняется в объекте WeakReference
Это приводит к проблеме. ThreadLocal будет переработан при сборке мусора, когда нет внешней строгой ссылки. Если поток, создавший ThreadLocal, продолжает работать, значение в объекте Entry может не перерабатываться постоянно, что приводит к утечкам памяти. .
Избегайте утечек памяти
Поскольку обнаружено, что существует скрытая опасность утечки памяти, существует естественная стратегия реагирования: вызов методов get() и set() класса ThreadLocal может очистить объект Entry, ключ которого равен null в ThreadLocalMap, так что соответствующее значение не будет достижимый с помощью GC Roots.В следующий раз он может быть переработан во время GC.Конечно, если вызывается метод удаления, соответствующий объект Entry обязательно будет удален.
Если метод удаления не вызывается явно после использования метода set в ThreadLocal, могут возникнуть утечки памяти, поэтому очень важно выработать хорошие привычки программирования.После использования ThreadLocal не забудьте вызвать метод удаления.
ThreadLocal<String> localName = new ThreadLocal();
try {
localName.set("JavaEdge");
// 其它业务逻辑
} finally {
localName.remove();
}
отступление
Во-первых, ThreadLocal не используется для решения проблемы многопоточного доступа к разделяемым объектам, в общем случае объект, передаваемый в поток через set(), является объектом, используемым самим потоком, и другим потокам это не нужно. доступ и не может получить к нему доступ. ; Каждый поток обращается к другому объекту.
**Кроме того, говорится, что ThreadLocal позволяет каждому потоку поддерживать независимый объект; это не реализовано с помощью set(), а объект создается операцией нового объекта в каждом потоке.Каждый поток создает один, а не A копировать или дублировать какой объект. **Сохраняйте ссылку вновь созданного объекта на карту каждого потока через set(), у каждого потока есть такая карта, при выполнении get() каждый поток берет ее из своей карты и кладет в объекты в соответствующих потоках извлекаются. Экземпляр ThreadLocal используется в качестве ключа карты.
Если вещь, введенная set(), изначально является одним и тем же объектом, совместно используемым несколькими потоками; тогда get() нескольких потоков получает сам общий объект, и проблема одновременного доступа все еще остается.
Типичное приложение ThreadLocal в Hibernate
private static final ThreadLocal threadSession = new ThreadLocal();
public static Session getSession() throws InfrastructureException {
Session s = (Session) threadSession.get();
try {
if (s == null) {
s = getSessionFactory().openSession();
threadSession.set(s);
}
} catch (HibernateException ex) {
throw new InfrastructureException(ex);
}
return s;
}
Сначала определите, был ли текущий поток помещен в сеанс, если нет, то передайтеsessionFactory().openSession()
для создания сеанса; затем сеансset()
Для потока он фактически помещается в текущий потокThreadLocalMap
;На данный момент единственная ссылка на сессию — это ThreadLocalMap в текущем потоке; threadSession — это ключ этого значения, для получения этой сессии можно передать threadSession.get(); операция, выполняемая в ней, — это собственно получение ThreadLocalMap в текущем потоке; Затем используйте threadSession в качестве ключа для извлечения соответствующего значения. Этот сеанс эквивалентен частной переменной потока, а не общедоступной.
Очевидно, что другие потоки не могут получить доступ к этому сеансу, и они могут получить доступ только к своей собственной карте ThreadLocalMap. Если сеанс используется несколькими потоками, он не беспорядочный.
Как это реализовать без ThreadLocal?
Может быть нужно создать сессию в экшене, а потом передавать сессию сервису и дао по одной, что достаточно хлопотно, или можно самому определить статическую карту, использовать в качестве ключа текущий поток, созданный session в качестве значения, и поместите его в В карте, это тоже должно работать, что также является мнением большинства людей, но на самом деле реализация ThreadLocal как раз наоборот, она имеет карту в каждом потоке и использует Экземпляр ThreadLocal в качестве ключа, чтобы количество элементов в каждой карте было очень большим, меньше, и при уничтожении потока соответствующие вещи также уничтожаются.
Короче,ThreadLocal
Он не используется для решения проблемы совместного доступа к объектам, а в основном для предоставления метода обслуживания объектов и удобного метода доступа к объектам, который позволяет избежать передачи параметров.
- У каждой нити своя
ThreadLocalMap
объект класса;
В нем может храниться собственный объект потока, и каждый поток может правильно обращаться к своему объекту. - общий
ThreadLocal
Статический экземпляр используется в качестве ключа для сохранения ссылок на разные объекты в ThreadLocalMap разных потоков, а затем для получения объекта, сохраненного потоком, с помощью метода get() статического экземпляра ThreadLocal везде в выполнении потока, избегая использования этот объект как параметр.Беда с передачей.
Конечно, если вы хотите поместить объект, совместно используемый потоком, в поток через set(), вы можете реализовать метод доступа, избегающий передачи параметров; но следует отметить, что тот же общий объект, полученный с помощью get(), Проблема параллельного доступа зависит от других способов ее решения, но, вообще говоря, к объектам, совместно используемым потоками, можно легко получить доступ, установив их как статические переменные определенного типа, и кажется, что нет необходимости помещать их в потоки.
Приложения ThreadLocal
Я думаю, что наиболее подходящим является доступ к объектам с несколькими экземплярами по потоку (каждый поток соответствует одному экземпляру), и этот объект используется во многих местах.
Вы можете видеть, что переменные в классе ThreadLocal имеют только эти три типа int:
private final int threadLocalHashCode = nextHashCode();
private static AtomicInteger nextHashCode =
new AtomicInteger();
private static final int HASH_INCREMENT = 0x61c88647;
И переменные, которые являются экземплярами ThreadLocal, толькоthreadLocalHashCodenextHashCodeа такжеHASH_INCREMENTна самом деле является статической переменной класса ThreadLocal
- HASH_INCREMENT — это константа, представляющая приращение значения threadLocalHashCode двух последовательно выделенных экземпляров ThreadLocal.
- nextHashCode представляет значение threadLocalHashCode следующего выделяемого экземпляра ThreadLocal.
Посмотрите, какие операции выполняются при создании экземпляра ThreadLocal, то есть new ThreadLocal(), метод построенияThreadLocal()
В нем нет операции, единственная операция это предложение
private final int threadLocalHashCode = nextHashCode();
Так что же делает nextHashCode()?
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
Он заключается в том, чтобы присвоить следующее значение hashCode класса ThreadLocal, то есть значение nextHashCode, для threadLocalHashCode экземпляра, а затем увеличить значение nextHashCode на значение HASH_INCREMENT. .
Следовательно, переменная экземпляра ThreadLocal — это только этот threadLocalHashCode, и он final, который используется для различения разных экземпляров ThreadLocal; класс ThreadLocal в основном используется как класс инструмента, так где же размещается объект, введенный set()?
Взгляните на метод set() выше и объедините два предложения, чтобы получить
ThreadLocalMap map = Thread.currentThread().threadLocals;
Этот класс ThreadLocalMap является внутренним классом, определенным в ThreadLocal, но его экземпляр используется в классе Thread:
public class Thread implements Runnable {
......
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
......
}
Посмотрите еще раз на это предложение:
if (map != null)
map.set(this, value);
То есть экземпляр ThreadLocal используется в качестве ключа, сохраняемый объект используется в качестве значения и устанавливается в ThreadLocalMap текущего потока.Метод get() также понимает код после чтения кода.
Ссылаться на
«Эффективный код: руководство по разработке на Java»