Если вы используете ThreadLocal, никогда не пишите обертку поверх него !!! Каждый разработчик, который хочет использовать переменную, должен знать, что это «ThreadLocal»
не ошибка
2
@Notabug Если вы явно, вы можете назвать свою переменную так, чтобы вы имели дело со значением ThreadLocal, например RequestUtils.getCurrentRequestThreadLocal(). Не сказать, что это очень элегантно, но это связано с тем, что сам ThreadLocal в большинстве случаев не очень элегантен.
whirlwin
@Sasha Можно ли использовать ThreadLocal для хранения следа выполнения
gaurav
Ответы:
864
Одно из возможных (и распространенных) применений - когда у вас есть какой-то объект, который не является потокобезопасным, но вы хотите избежать синхронизации доступа к этому объекту (я смотрю на вас, SimpleDateFormat ). Вместо этого дайте каждому потоку свой экземпляр объекта.
Например:
publicclassFoo{// SimpleDateFormat is not thread-safe, so give one to each threadprivatestaticfinalThreadLocal<SimpleDateFormat> formatter =newThreadLocal<SimpleDateFormat>(){@OverrideprotectedSimpleDateFormat initialValue(){returnnewSimpleDateFormat("yyyyMMdd HHmm");}};publicString formatIt(Date date){return formatter.get().format(date);}}
Другая альтернатива синхронизации или threadlocal - сделать переменную локальной переменной. Местные переменные всегда безопасны для потоков. Я предполагаю, что делать DateFormats локально плохой практикой, потому что создавать их дорого, но я никогда не видел надежных показателей по этой теме.
@overthink Есть ли причина объявить ThreadLocal статическим и окончательным, я имею в виду производительность или что-то в этом роде?
Сачин Горад
4
Метод ThreadLocal.get () будет вызывать ThreadLocal.initialValue () (один раз) для каждого потока, что означает, что для каждого потока создается объект SimpleDateFormat. Не лучше ли просто иметь SimpleDateFormat в качестве локальных переменных (так как нам не приходится иметь дело с проблемами сбора мусора)?
sudeepdino008
3
Только будьте осторожны, чтобы не использовать двойную инициализацию для вашего SimpleDateFormat, потому что это создаст анонимный класс, чтобы ваш загрузчик классов не мог быть собран мусором. утечка памяти: вернуть новый SimpleDateFormat () {{applyPattern ("yyyyMMdd HHmm")}};
user1944408
427
Поскольку a ThreadLocal- это ссылка на данные в пределах данного Thread, вы можете столкнуться с утечками при загрузке классов при использовании ThreadLocals на серверах приложений с использованием пулов потоков. Вы должны быть очень осторожны , об очистке любых ThreadLocalS вас get()или set()с помощью ThreadLocal«s remove()метода.
Если вы не выполните очистку, когда закончите, все ссылки на классы, загруженные как часть развернутого веб-приложения, останутся в постоянной куче и никогда не будут собирать мусор. Повторное развертывание / удаление веб-приложения не приведет к очистке каждой Threadссылки на класс (ы) вашего веб-приложения, поскольку Threadоно не принадлежит вашему веб-приложению. Каждое последующее развертывание создаст новый экземпляр класса, который никогда не будет собирать мусор.
Вы получите исключение java.lang.OutOfMemoryError: PermGen spaceнехватки памяти из-за того, что после некоторого поиска в Google, скорее всего, он просто увеличится -XX:MaxPermSizeвместо исправления ошибки.
Алекс Вассер перенес свой блог. Вот текущая ссылка на статью утечки памяти.
Кенстер
12
Кажется, что нить, на которую ссылается Жюльен, переместилась сюда , и ее стоит прочитать…
Donal Fellows
28
Это очень много голосов за ответ, который, хотя и информативен, никоим образом не отвечает на вопрос.
Робин
8
Теперь, когда PermGen убит Java 8, это как-то изменит этот ответ?
Evil Стиральная машина
13
@ Робин: я не согласен. Вопрос заключается в том, как правильно использовать ThreadLocal, чтобы получить полное представление о любой концепции (ThreadLocal здесь), также важно понять, как ее не использовать, и рискует использовать ее небрежно, и в этом заключается ответ Фила. Я рад, что он затронул этот вопрос, который не отражен ни в каких других ответах. Все эти голоса вполне заслужены. Я считаю, что SO должен создавать понимание концепций, а не просто быть сайтом контроля качества.
Саураб Патил
166
Многие фреймворки используют ThreadLocals для поддержки некоторого контекста, связанного с текущим потоком. Например, когда текущая транзакция хранится в ThreadLocal, вам не нужно передавать ее в качестве параметра через каждый вызов метода, если кому-то из стека требуется доступ к нему. Веб-приложения могут хранить информацию о текущем запросе и сеансе в ThreadLocal, чтобы приложение имело к ним легкий доступ. С Guice вы можете использовать ThreadLocals при реализации пользовательских областей действия для внедренных объектов ( области сервлетов Guice по умолчанию, скорее всего, также используют их).
ThreadLocals - это один из видов глобальных переменных (хотя и немного менее злой, потому что они ограничены одним потоком), поэтому вы должны быть осторожны при их использовании, чтобы избежать нежелательных побочных эффектов и утечек памяти. Разработайте свои API так, чтобы значения ThreadLocal всегда автоматически очищались, когда они больше не нужны, и что неправильное использование API будет невозможно (например, так ). ThreadLocals могут быть использованы для того, чтобы сделать код чище, и в некоторых редких случаях они являются единственным способом заставить что-то работать (в моем текущем проекте было два таких случая; они описаны здесь в разделе «Статические поля и глобальные переменные»).
Именно так инфраструктура exPOJO (www.expojo.com) позволяет получить доступ к ORM Session / PersistenceManager, не требуя затрат на аннотации и внедрение. Это что-то вроде «инъекции потока» вместо «инъекции объекта». Он обеспечивает доступ к зависимостям без необходимости (и накладных расходов) их встраивания в каждый объект, который может нуждаться в этих зависимостях. Удивительно, как «легкий вес» вы можете сделать DI-фреймворк, когда вы используете
инжекцию
27
Почему мне пришлось прокрутить вниз, чтобы найти этот важный ответ !?
Джереми Стейн
1
@Esko, в вашем проекте использование ThreadLocal в хэш-коде и равно не нужно. Лучшим подходом является создание или передача ссылки на хэш-код / функцию equals всякий раз, когда это необходимо. Таким образом, вы можете полностью избежать необходимости взлома ThreadLocal.
В Java, если у вас есть данные, которые могут варьироваться в зависимости от потока, вы можете передать эти данные каждому методу, который нуждается (или может понадобиться) в них, или связать данные с потоком. Передача данных везде может быть работоспособной, если все ваши методы уже должны обойти общую переменную context.
Если это не так, вы можете не загромождать сигнатуры вашего метода дополнительным параметром. В не поточном мире вы можете решить проблему с помощью Java-эквивалента глобальной переменной. В многопоточном слове эквивалентом глобальной переменной является локальная переменная потока.
Поэтому вы должны избегать локальных потоков так же, как вы избегаете глобальных. Я не могу согласиться с тем, что нормально создавать глобальные переменные (локальные потоки) вместо передачи значений, людям это не нравится, потому что это часто выявляет проблемы архитектуры, которые они не хотят исправлять.
Хуан Мендес
10
Возможно ... Это может быть полезно, хотя, если у вас есть огромная, существующая кодовая база, к которой вы должны добавить новую опорную точку, которая должна передаваться везде , например, контекст сеанса, транзакция БД, зарегистрированный пользователь и т. Д. .
Корнел Masson
6
Престижность использования данных вместо данных.
глубокое
Это сделало меня любопытным. 'datum' is the singular form and 'data' is the plural form.
Сиддхартха
23
В книге Java Concurrency in Practice есть очень хороший пример . Где автор ( Джошуа Блох ) объясняет, как ограничение потоков является одним из самых простых способов обеспечения безопасности потоков, а ThreadLocal является более формальным средством поддержания ограничения потоков. В конце он также объясняет, как люди могут злоупотреблять им, используя его как глобальные переменные.
Я скопировал текст из упомянутой книги, но код 3.10 отсутствует, так как не так важно понимать, где следует использовать ThreadLocal.
Локальные переменные потока часто используются для предотвращения совместного использования в проектах, основанных на изменчивых синглетонах или глобальных переменных. Например, однопоточное приложение может поддерживать глобальное соединение с базой данных, которое инициализируется при запуске, чтобы избежать необходимости передавать Соединение каждому методу. Поскольку соединения JDBC могут быть не поточно-ориентированными, многопоточное приложение, использующее глобальное соединение без дополнительной координации, также не является поточно-ориентированным. Используя ThreadLocal для хранения соединения JDBC, как в ConnectionHolder в Листинге 3.10, каждый поток будет иметь свое собственное соединение.
ThreadLocal широко используется при реализации каркасов приложений. Например, контейнеры J2EE связывают контекст транзакции с исполняющим потоком на время вызова EJB. Это легко реализовать с помощью статического Thread-Local, содержащего контекст транзакции: когда код платформы должен определить, какая транзакция выполняется в данный момент, он выбирает контекст транзакции из этого ThreadLocal. Это удобно тем, что уменьшает необходимость передавать информацию о контексте выполнения в каждый метод, но связывает любой код, который использует этот механизм, с платформой.
Легко злоупотреблять ThreadLocal, рассматривая его свойство ограничения потока как лицензию на использование глобальных переменных или как средство создания «скрытых» аргументов метода. Как и глобальные переменные, локальные переменные потока могут отвлекать от повторного использования и вводить скрытые связи между классами, поэтому их следует использовать с осторожностью.
По сути, когда вам нужно , чтобы значение переменной зависело от текущего потока, и вам неудобно прикреплять значение к потоку каким-либо другим способом (например, потоком подклассов).
Типичный случай - когда какой-то другой фреймворк создал поток, в котором выполняется ваш код, например, контейнер сервлета, или когда просто имеет смысл использовать ThreadLocal, потому что ваша переменная тогда «на своем логическом месте» (а не переменная висит в подклассе Thread или в другой хэш-карте).
Некоторые люди рекомендуют использовать ThreadLocal как способ присоединения «идентификатора потока» к каждому потоку в определенных параллельных алгоритмах, где вам нужен номер потока (см., Например, Herlihy & Shavit). В таких случаях убедитесь, что вы действительно получаете выгоду!
Можно ли использовать ThreadLocal для хранения трассы выполнения
gaurav
13
ThreadLocal в Java был представлен в JDK 1.2, но позднее был обобщен в JDK 1.5 для обеспечения безопасности типов в переменной ThreadLocal.
ThreadLocal может быть связан с областью действия Thread, весь код, который выполняется Thread, имеет доступ к переменным ThreadLocal, но два потока не могут видеть друг друга переменную ThreadLocal.
Каждый поток содержит эксклюзивную копию переменной ThreadLocal, которая получает право на сборку мусора после завершения или прекращения потока, как правило, или из-за какого-либо исключения, учитывая, что переменная ThreadLocal не имеет никаких других живых ссылок.
Переменные ThreadLocal в Java, как правило, являются частными статическими полями в классах и поддерживают свое состояние внутри Thread.
Можно ли использовать ThreadLocal для хранения трассы выполнения
gaurav
11
В документации очень хорошо сказано: «каждый поток, который обращается к [локальной переменной потока] (через свой метод get или set), имеет свою собственную, независимо инициализированную копию переменной».
Вы используете один, когда каждый поток должен иметь свою собственную копию чего-либо. По умолчанию данные распределяются между потоками.
По умолчанию статические объекты или объекты, явно передаваемые между потоками, где оба потока имеют ссылку на один и тот же объект, являются общими. Объекты, объявленные локально в потоке, не являются общими (они локальны для стека потока). Просто хотел уточнить это.
Ян Варли
@IanVarley: Спасибо, что указали на это. Итак, если у нас есть переменная, которая объявлена и используется в классе MyThread, то нужен ли нам в этом случае ThreadLocal? Пример: класс MyThread extends Thread {String var1 ;, int var2; } в этом случае var1 и var2 будут частью собственного стека Thread и не будут использоваться совместно, поэтому требуется ли нам в этом случае ThreadLocal? Я могу быть совершенно не прав в моем понимании, пожалуйста, руководство.
Джаеш
4
Если каждый поток хочет иметь свою собственную копию чего-либо, почему он просто не может быть объявлен локальным (который всегда безопасен для потоков)?
sudeepdino008
@ sudeepdino008 у вас не всегда есть контроль над созданием этих потоков (фреймворки веб-приложений, библиотеки потоков)
JMess
10
Сервер Webapp может хранить пул потоков, и ThreadLocalпеременная должна быть удалена до ответа клиенту, поэтому текущий поток может быть повторно использован при следующем запросе.
Два случая использования, где можно использовать переменную threadlocal -
1- Когда у нас есть требование связать состояние с потоком (например, идентификатор пользователя или идентификатор транзакции). Это обычно происходит с веб-приложением, когда каждый запрос к сервлету имеет уникальный идентификатор транзакции, связанный с ним.
// This class will provide a thread local variable which// will provide a unique ID for each threadclassThreadId{// Atomic integer containing the next thread ID to be assignedprivatestaticfinalAtomicInteger nextId =newAtomicInteger(0);// Thread local variable containing each thread's IDprivatestaticfinalThreadLocal<Integer> threadId =ThreadLocal.<Integer>withInitial(()->{return nextId.getAndIncrement();});// Returns the current thread's unique ID, assigning it if necessarypublicstaticint get(){return threadId.get();}}
Обратите внимание, что здесь метод withInitial реализован с использованием лямбда-выражения.
2- Другой случай использования - когда мы хотим иметь потокобезопасный экземпляр, и мы не хотим использовать синхронизацию, поскольку затраты на производительность с синхронизацией больше. Одним из таких случаев является использование SimpleDateFormat. Так как SimpleDateFormat не является потокобезопасным, поэтому мы должны предоставить механизм, чтобы сделать его потокобезопасным.
publicclassThreadLocalDemo1implementsRunnable{// threadlocal variable is createdprivatestaticfinalThreadLocal<SimpleDateFormat> dateFormat =newThreadLocal<SimpleDateFormat>(){@OverrideprotectedSimpleDateFormat initialValue(){System.out.println("Initializing SimpleDateFormat for - "+Thread.currentThread().getName());returnnewSimpleDateFormat("dd/MM/yyyy");}};publicstaticvoid main(String[] args){ThreadLocalDemo1 td =newThreadLocalDemo1();// Two threads are createdThread t1 =newThread(td,"Thread-1");Thread t2 =newThread(td,"Thread-2");
t1.start();
t2.start();}@Overridepublicvoid run(){System.out.println("Thread run execution started for "+Thread.currentThread().getName());System.out.println("Date formatter pattern is "+ dateFormat.get().toPattern());System.out.println("Formatted date is "+ dateFormat.get().format(newDate()));}}
Я до сих пор не видел преимущества использования ThreadLocal для генерации уникального идентификатора в первом примере, если вся ваша цель - возвращать возрастающее уникальное целочисленное значение. `` `public class ThreadId {// Атомное целое число, содержащее следующий идентификатор потока, которому будет назначено личное статическое окончание AtomicInteger nextId = new AtomicInteger (0); // Возвращает уникальный идентификатор текущего потока, присваивая его при необходимости public static int get () {return nextId..getAndIncrement (); }} `` `
dashenswen
7
Начиная с выпуска Java 8, существует более декларативный способ инициализации ThreadLocal:
ThreadLocal<Cipher> local =ThreadLocal.withInitial(()->"init value");
До выхода Java 8 вам приходилось делать следующее:
ThreadLocal<String> local =newThreadLocal<String>(){@OverrideprotectedString initialValue(){return"init value";}};
Кроме того, если метод создания экземпляра (конструктор, метод фабрики) класса, для которого он используется ThreadLocal, не принимает никаких параметров, вы можете просто использовать ссылки на методы (представленные в Java 8):
classNotThreadSafe{// no parameterspublicNotThreadSafe(){}}ThreadLocal<NotThreadSafe> container =ThreadLocal.withInitial(NotThreadSafe::new);
Примечание.
Оценка является ленивой, поскольку вы передаете java.util.function.Supplierлямбду, которая оценивается только тогда, когда ThreadLocal#getвызывается, но значение ранее не оценивалось.
Вы должны быть очень осторожны с паттерном ThreadLocal. Есть несколько основных недостатков, подобных упомянутому Филу, но один, который не был упомянут, - убедиться, что код, который устанавливает контекст ThreadLocal, не является «повторным».
Плохие вещи могут случиться, когда код, который устанавливает информацию, запускается второй или третий раз, потому что информация в вашем потоке может начать изменяться, когда вы этого не ожидаете. Поэтому позаботьтесь о том, чтобы информация ThreadLocal не была установлена до того, как вы установите ее снова.
Повторный вход не является проблемой, если код готов справиться с этим. При вводе запишите, установлена ли переменная, и при выходе восстановите ее предыдущее значение (если оно было) или удалите его (если нет).
суперкат
1
@ Джефф, Чувак, это верно для каждого кода, который ты пишешь, а не только для ThreadLocalшаблона. Если вы сделаете это F(){ member=random(); F2(); write(member); }и F2 переопределит элемент новым значением, то, очевидно write(member), больше не будет писать число, которое вы random()редактировали. Это буквально просто здравый смысл. Точно так же, если вы это сделаете F(){ F(); }, то удачи в вашем бесконечном цикле! Это верно везде и не является специфическим для ThreadLocal.
Pacerier
@ Джефф, пример кода?
Пейсер
5
когда?
Когда объект не является потокобезопасным, вместо синхронизации, которая препятствует масштабируемости, передайте по одному объекту каждому потоку и оставьте его область видимости потока, то есть ThreadLocal. Одним из наиболее часто используемых, но не поточно-ориентированных объектов являются база данных Connection и JMSConnection.
Как ?
Одним из примеров является среда Spring, в которой ThreadLocal активно используется для скрытого управления транзакциями путем сохранения этих объектов подключения в переменных ThreadLocal. На высоком уровне, когда транзакция запускается, она получает соединение (и отключает автоматическую фиксацию) и сохраняет его в ThreadLocal. при дальнейших вызовах БД он использует то же соединение для связи с БД. В конце он берет соединение от ThreadLocal и фиксирует (или откатывает) транзакцию и освобождает соединение.
Я думаю, что log4j также использует ThreadLocal для поддержки MDC.
ThreadLocal полезно, когда вы хотите иметь какое-то состояние, которое не должно быть общим для разных потоков, но оно должно быть доступно каждому потоку в течение всего его срока службы.
В качестве примера представим веб-приложение, в котором каждый запрос обслуживается отдельным потоком. Представьте, что для каждого запроса вам нужно многократно обрабатывать один фрагмент данных, что довольно дорого для вычисления. Однако эти данные могли изменяться для каждого входящего запроса, что означает, что вы не можете использовать простой кэш. Простым и быстрым решением этой проблемы было бы иметь ThreadLocalпеременную, содержащую доступ к этим данным, так что вам придется рассчитывать ее только один раз для каждого запроса. Конечно, эту проблему также можно решить без использования ThreadLocal, но я придумал ее для наглядности.
Тем не менее, имейте в виду, что ThreadLocalэто по существу форма глобального государства. В результате он имеет много других последствий и должен использоваться только после рассмотрения всех других возможных решений.
ThreadLocals НЕ являются глобальным состоянием, если вы не сделаете его глобальным состоянием. Они практически всегда доступны стековым ресурсом. Вы можете смоделировать поток локально, просто передав эту переменную во все ваши методы (которая запаздывает); это не делает его глобальным государством ...
Enerccio
Это форма глобального состояния в том смысле, что она доступна из любого места кода (в контексте того же потока). Это происходит со всеми последствиями, такими как неспособность рассуждать о том, кто читает и записывает это значение. Использование параметра функции - не запаздывающая вещь, это хорошая практика продвижения чистых интерфейсов. Однако я согласен с вами, что передача параметра по всей глубине вашей кодовой базы - это запах кода. Но я также считаю, что во многих случаях использование ThreadLocal - это прежде всего запах кода, который привел вас сюда, так что это то, что следует пересмотреть.
Димос
Он может быть просто частью состояния объекта, и его не нужно использовать глобально. Конечно, вы получаете немного накладных расходов, но если несколько объектов должны иметь разное состояние для каждого потока, вы можете использовать в ThreadLocalкачестве поля объекта ...
Enerccio
Вы правы. Я, вероятно, наткнулся на несколько злоупотреблений ThreadLocal, где это было доступно во всем мире. Как вы сказали, он все еще может использоваться как поле класса, ограничивающее видимость.
Димос
4
Здесь нет ничего нового, но сегодня я обнаружил, что ThreadLocalэто очень полезно при использовании Bean Validation в веб-приложении. Сообщения проверки локализованы, но по умолчанию используются Locale.getDefault(). Вы можете настроить Validatorс другим MessageInterpolator, но нет способа указать, Localeкогда вы звоните validate. Таким образом, вы можете создать статический ThreadLocal<Locale>(или, еще лучше, общий контейнер с другими вещами, которые вам могут понадобиться), ThreadLocalа затем сделать свой MessageInterpolatorвыбор Localeиз этого. Следующий шаг - написать объект, ServletFilterкоторый использует значение сеанса или request.getLocale()выбрать локаль и сохранить это в вашей ThreadLocalссылке.
Как уже упоминалось @unknown (google), он используется для определения глобальной переменной, в которой указанное значение может быть уникальным в каждом потоке. Обычно его использование влечет за собой хранение некоторой контекстной информации, которая связана с текущим потоком выполнения.
Мы используем его в среде Java EE для передачи идентификатора пользователя классам, которые не поддерживают Java EE (не имеют доступа к HttpSession или EJB SessionContext). Таким образом, код, который использует идентификатор для операций, основанных на безопасности, может получить доступ к идентификатору из любого места, без необходимости явно передавать его при каждом вызове метода.
Цикл операций запрос / ответ в большинстве вызовов Java EE упрощает этот тип использования, поскольку он дает четко определенные точки входа и выхода для установки и сброса ThreadLocal.
ThreadLocal обеспечит синхронизацию доступа к изменяемому объекту несколькими потоками в несинхронизированном методе, что означает, что изменяемый объект будет неизменным внутри метода.
Это достигается путем предоставления нового экземпляра изменяемого объекта для каждого потока, пытающегося получить к нему доступ. Так что это локальная копия каждого потока. Это некоторый хак при создании переменной экземпляра в методе, к которому нужно обращаться как в локальной переменной. Как вы знаете, локальная переменная метода доступна только для потока, есть одно отличие; Локальные переменные метода не будут доступны для потока после завершения выполнения метода, поскольку изменяемый объект, совместно используемый с threadlocal, будет доступен для нескольких методов, пока мы его не очистим.
По определению:
Класс ThreadLocal в Java позволяет создавать переменные, которые могут быть прочитаны и записаны только одним потоком. Таким образом, даже если два потока выполняют один и тот же код, а код имеет ссылку на переменную ThreadLocal, эти два потока не могут видеть переменные ThreadLocal друг друга.
Каждый Threadв Java содержится ThreadLocalMapв нем.
куда
Key=OneThreadLocal object shared across threads.
value =Mutable object which has to be used synchronously,this will be instantiated for each thread.
Достижение ThreadLocal:
Теперь создайте класс-оболочку для ThreadLocal, который будет содержать изменяемый объект, как показано ниже (с или без initialValue()). Теперь getter и setter этой оболочки будут работать с экземпляром threadlocal вместо изменяемого объекта.
Если getter () из threadlocal не нашел никакого значения с в threadlocalmap из Thread; тогда он вызовет initialValue (), чтобы получить свою частную копию относительно потока.
classSimpleDateFormatInstancePerThread{privatestaticfinalThreadLocal<SimpleDateFormat> dateFormatHolder =newThreadLocal<SimpleDateFormat>(){@OverrideprotectedSimpleDateFormat initialValue(){SimpleDateFormat dateFormat =newSimpleDateFormat("yyyy-MM-dd"){
UUID id = UUID.randomUUID();@OverridepublicString toString(){return id.toString();};};System.out.println("Creating SimpleDateFormat instance "+ dateFormat +" for Thread : "+Thread.currentThread().getName());return dateFormat;}};/*
* Every time there is a call for DateFormat, ThreadLocal will return calling
* Thread's copy of SimpleDateFormat
*/publicstaticDateFormat getDateFormatter(){return dateFormatHolder.get();}publicstaticvoid cleanup(){
dateFormatHolder.remove();}}
Теперь wrapper.getDateFormatter()вызовем threadlocal.get()и проверим, currentThread.threadLocalMapсодержит ли это (threadlocal) экземпляр.
Если да, вернуть значение (SimpleDateFormat) для соответствующего
экземпляра локального потока, иначе добавьте карту с этим экземпляром локального потока, initialValue ().
При этом безопасность потока достигается на этом изменяемом классе; каждый поток работает со своим изменяемым экземпляром, но с тем же экземпляром ThreadLocal. Означает, что все потоки будут использовать один и тот же экземпляр ThreadLocal в качестве ключа, но другой экземпляр SimpleDateFormat в качестве значения.
Локальные переменные потока часто используются для предотвращения совместного использования в проектах, основанных на изменчивых синглетонах или глобальных переменных.
Он может использоваться в таких случаях, как создание отдельного соединения JDBC для каждого потока, когда вы не используете пул соединений.
Когда вы вызываете getConnection, он возвращает соединение, связанное с этим потоком. То же самое можно сделать с другими свойствами, такими как dateformat, контекст транзакции, который вы не хотите разделять между потоками.
Вы могли бы также использовать локальные переменные для них, но эти ресурсы обычно занимают время при создании, поэтому вы не хотите создавать их снова и снова, когда выполняете с ними некоторую бизнес-логику. Тем не менее, значения ThreadLocal хранятся в самом объекте потока, и как только поток подвергается сборке мусора, эти значения также исчезают.
Эта ссылка очень хорошо объясняет использование ThreadLocal.
В этом примере основная проблема: кто отвечает за закрытие соединения? Вместо того чтобы создавать это соединение в качестве начального значения, разрешите потребителю этого соединения явно создать соединение и связать его с потоком через ThreadLocal. Создатель соединения также отвечает за закрытие. Это не ясно в этом примере. Создание и привязка соединения также могут быть скрыты в простой структуре с помощью чего-то вроде Transaction.begin () и Transaction.end ().
Рик-Райнер Людвиг
3
Существует 3 сценария использования помощника класса, такого как SimpleDateFormat, в многопоточном коде, лучший из которых - использование ThreadLocal.
Сценарии
1- Использование объекта общего ресурса с помощью механизма блокировки или синхронизации , который замедляет работу приложения
2- Использование в качестве локального объекта внутри метода
В этом сценарии, если у нас есть 4 потока, каждый из которых вызывает метод 1000 раз, мы создаем 4000 объектов SimpleDateFormat и ожидаем, пока GC их сотрет.
3- Использование ThreadLocal
если у нас есть 4 потока, и мы дали каждому потоку один экземпляр SimpleDateFormat,
то есть у нас 4 потока , 4 объекта SimpleDateFormat.
Нет необходимости в механизме блокировки, создании и уничтожении объектов. (Хорошая временная сложность и пространственная сложность)
Класс ThreadLocal в Java позволяет создавать переменные, которые могут быть прочитаны и записаны только одним потоком. Таким образом, даже если два потока выполняют один и тот же код, а код имеет ссылку на переменную ThreadLocal, эти два потока не могут видеть переменные ThreadLocal друг друга.
[Для справки] ThreadLocal не может решить проблемы обновления общего объекта. Рекомендуется использовать объект staticThreadLocal, который используется всеми операциями в одном потоке. [Обязательный] метод remove () должен быть реализован переменными ThreadLocal, особенно при использовании пулов потоков, в которых потоки часто используются повторно. В противном случае это может повлиять на последующую бизнес-логику и вызвать непредвиденные проблемы, такие как утечка памяти.
Кешируя, иногда вам приходится вычислять одно и то же значение много времени, поэтому, сохраняя последний набор входных данных для метода и результата, вы можете ускорить код. Используя Thread Local Storage, вам не нужно думать о блокировке.
ThreadLocal - это специально предоставляемая функциональность JVM для предоставления изолированного пространства хранения только для потоков. подобно значению переменной области видимости, привязанной только к данному экземпляру класса. каждый объект имеет свои единственные значения, и они не могут видеть значения друг друга. как и концепция переменных ThreadLocal, они являются локальными для потока в том смысле, что экземпляры объектов, которые другие потоки, кроме той, которая его создала, не могут ее видеть. Посмотреть здесь
import java.util.concurrent.atomic.AtomicInteger;import java.util.stream.IntStream;publicclassThreadId{privatestaticfinalAtomicInteger nextId =newAtomicInteger(1000);// Thread local variable containing each thread's IDprivatestaticfinalThreadLocal<Integer> threadId =ThreadLocal.withInitial(()-> nextId.getAndIncrement());// Returns the current thread's unique ID, assigning it if necessarypublicstaticint get(){return threadId.get();}publicstaticvoid main(String[] args){newThread(()->IntStream.range(1,3).forEach(i ->{System.out.println(Thread.currentThread().getName()+" >> "+newThreadId().get());})).start();newThread(()->IntStream.range(1,3).forEach(i ->{System.out.println(Thread.currentThread().getName()+" >> "+newThreadId().get());})).start();newThread(()->IntStream.range(1,3).forEach(i ->{System.out.println(Thread.currentThread().getName()+" >> "+newThreadId().get());})).start();}}
Threadlocal обеспечивает очень простой способ достижения повторного использования объектов с нулевой стоимостью.
У меня была ситуация, когда несколько потоков создавали образ изменяемого кэша при каждом уведомлении об обновлении.
Я использовал Threadlocal в каждом потоке, а затем каждому потоку нужно было просто сбросить старое изображение и затем обновлять его из кэша при каждом уведомлении об обновлении.
Обычные повторно используемые объекты из пулов объектов связаны с затратами на безопасность потоков, в то время как в этом подходе их нет.
RequestUtils.getCurrentRequestThreadLocal()
. Не сказать, что это очень элегантно, но это связано с тем, что сам ThreadLocal в большинстве случаев не очень элегантен.Ответы:
Одно из возможных (и распространенных) применений - когда у вас есть какой-то объект, который не является потокобезопасным, но вы хотите избежать синхронизации доступа к этому объекту (я смотрю на вас, SimpleDateFormat ). Вместо этого дайте каждому потоку свой экземпляр объекта.
Например:
Документация .
источник
SimpleDateFormat
. Возможно, лучше использовать потокобезопасную альтернативу . Если вы согласны с тем, что одиночные игры плохие, тоThreadLocal
еще хуже.Поскольку a
ThreadLocal
- это ссылка на данные в пределах данногоThread
, вы можете столкнуться с утечками при загрузке классов при использованииThreadLocal
s на серверах приложений с использованием пулов потоков. Вы должны быть очень осторожны , об очистке любыхThreadLocal
S васget()
илиset()
с помощьюThreadLocal
«sremove()
метода.Если вы не выполните очистку, когда закончите, все ссылки на классы, загруженные как часть развернутого веб-приложения, останутся в постоянной куче и никогда не будут собирать мусор. Повторное развертывание / удаление веб-приложения не приведет к очистке каждой
Thread
ссылки на класс (ы) вашего веб-приложения, посколькуThread
оно не принадлежит вашему веб-приложению. Каждое последующее развертывание создаст новый экземпляр класса, который никогда не будет собирать мусор.Вы получите исключение
java.lang.OutOfMemoryError: PermGen space
нехватки памяти из-за того, что после некоторого поиска в Google, скорее всего, он просто увеличится-XX:MaxPermSize
вместо исправления ошибки.Если вы в конечном итоге столкнулись с этими проблемами, вы можете определить, какой поток и класс сохраняют эти ссылки, с помощью анализатора памяти Eclipse и / или следуя руководству и последующим указаниям Frank Kieviet .
Обновление: вновь обнаружил запись в блоге Алекса Вассера, которая помогла мне отследить некоторые
ThreadLocal
проблемы, которые у меня были.источник
Многие фреймворки используют ThreadLocals для поддержки некоторого контекста, связанного с текущим потоком. Например, когда текущая транзакция хранится в ThreadLocal, вам не нужно передавать ее в качестве параметра через каждый вызов метода, если кому-то из стека требуется доступ к нему. Веб-приложения могут хранить информацию о текущем запросе и сеансе в ThreadLocal, чтобы приложение имело к ним легкий доступ. С Guice вы можете использовать ThreadLocals при реализации пользовательских областей действия для внедренных объектов ( области сервлетов Guice по умолчанию, скорее всего, также используют их).
ThreadLocals - это один из видов глобальных переменных (хотя и немного менее злой, потому что они ограничены одним потоком), поэтому вы должны быть осторожны при их использовании, чтобы избежать нежелательных побочных эффектов и утечек памяти. Разработайте свои API так, чтобы значения ThreadLocal всегда автоматически очищались, когда они больше не нужны, и что неправильное использование API будет невозможно (например, так ). ThreadLocals могут быть использованы для того, чтобы сделать код чище, и в некоторых редких случаях они являются единственным способом заставить что-то работать (в моем текущем проекте было два таких случая; они описаны здесь в разделе «Статические поля и глобальные переменные»).
источник
В Java, если у вас есть данные, которые могут варьироваться в зависимости от потока, вы можете передать эти данные каждому методу, который нуждается (или может понадобиться) в них, или связать данные с потоком. Передача данных везде может быть работоспособной, если все ваши методы уже должны обойти общую переменную context.
Если это не так, вы можете не загромождать сигнатуры вашего метода дополнительным параметром. В не поточном мире вы можете решить проблему с помощью Java-эквивалента глобальной переменной. В многопоточном слове эквивалентом глобальной переменной является локальная переменная потока.
источник
'datum' is the singular form and 'data' is the plural form.
В книге Java Concurrency in Practice есть очень хороший пример . Где автор ( Джошуа Блох ) объясняет, как ограничение потоков является одним из самых простых способов обеспечения безопасности потоков, а ThreadLocal является более формальным средством поддержания ограничения потоков. В конце он также объясняет, как люди могут злоупотреблять им, используя его как глобальные переменные.
Я скопировал текст из упомянутой книги, но код 3.10 отсутствует, так как не так важно понимать, где следует использовать ThreadLocal.
источник
По сути, когда вам нужно , чтобы значение переменной зависело от текущего потока, и вам неудобно прикреплять значение к потоку каким-либо другим способом (например, потоком подклассов).
Типичный случай - когда какой-то другой фреймворк создал поток, в котором выполняется ваш код, например, контейнер сервлета, или когда просто имеет смысл использовать ThreadLocal, потому что ваша переменная тогда «на своем логическом месте» (а не переменная висит в подклассе Thread или в другой хэш-карте).
На моем веб-сайте есть дальнейшее обсуждение и примеры использования ThreadLocal, которые также могут представлять интерес.
Некоторые люди рекомендуют использовать ThreadLocal как способ присоединения «идентификатора потока» к каждому потоку в определенных параллельных алгоритмах, где вам нужен номер потока (см., Например, Herlihy & Shavit). В таких случаях убедитесь, что вы действительно получаете выгоду!
источник
ThreadLocal в Java был представлен в JDK 1.2, но позднее был обобщен в JDK 1.5 для обеспечения безопасности типов в переменной ThreadLocal.
ThreadLocal может быть связан с областью действия Thread, весь код, который выполняется Thread, имеет доступ к переменным ThreadLocal, но два потока не могут видеть друг друга переменную ThreadLocal.
Каждый поток содержит эксклюзивную копию переменной ThreadLocal, которая получает право на сборку мусора после завершения или прекращения потока, как правило, или из-за какого-либо исключения, учитывая, что переменная ThreadLocal не имеет никаких других живых ссылок.
Переменные ThreadLocal в Java, как правило, являются частными статическими полями в классах и поддерживают свое состояние внутри Thread.
Подробнее: ThreadLocal в Java - пример программы и учебник
источник
В документации очень хорошо сказано: «каждый поток, который обращается к [локальной переменной потока] (через свой метод get или set), имеет свою собственную, независимо инициализированную копию переменной».
Вы используете один, когда каждый поток должен иметь свою собственную копию чего-либо. По умолчанию данные распределяются между потоками.
источник
Сервер Webapp может хранить пул потоков, и
ThreadLocal
переменная должна быть удалена до ответа клиенту, поэтому текущий поток может быть повторно использован при следующем запросе.источник
Два случая использования, где можно использовать переменную threadlocal -
1- Когда у нас есть требование связать состояние с потоком (например, идентификатор пользователя или идентификатор транзакции). Это обычно происходит с веб-приложением, когда каждый запрос к сервлету имеет уникальный идентификатор транзакции, связанный с ним.
Обратите внимание, что здесь метод withInitial реализован с использованием лямбда-выражения.
2- Другой случай использования - когда мы хотим иметь потокобезопасный экземпляр, и мы не хотим использовать синхронизацию, поскольку затраты на производительность с синхронизацией больше. Одним из таких случаев является использование SimpleDateFormat. Так как SimpleDateFormat не является потокобезопасным, поэтому мы должны предоставить механизм, чтобы сделать его потокобезопасным.
источник
Начиная с выпуска Java 8, существует более декларативный способ инициализации
ThreadLocal
:До выхода Java 8 вам приходилось делать следующее:
Кроме того, если метод создания экземпляра (конструктор, метод фабрики) класса, для которого он используется
ThreadLocal
, не принимает никаких параметров, вы можете просто использовать ссылки на методы (представленные в Java 8):Примечание. Оценка является ленивой, поскольку вы передаете
java.util.function.Supplier
лямбду, которая оценивается только тогда, когдаThreadLocal#get
вызывается, но значение ранее не оценивалось.источник
Вы должны быть очень осторожны с паттерном ThreadLocal. Есть несколько основных недостатков, подобных упомянутому Филу, но один, который не был упомянут, - убедиться, что код, который устанавливает контекст ThreadLocal, не является «повторным».
Плохие вещи могут случиться, когда код, который устанавливает информацию, запускается второй или третий раз, потому что информация в вашем потоке может начать изменяться, когда вы этого не ожидаете. Поэтому позаботьтесь о том, чтобы информация ThreadLocal не была установлена до того, как вы установите ее снова.
источник
ThreadLocal
шаблона. Если вы сделаете этоF(){ member=random(); F2(); write(member); }
и F2 переопределит элемент новым значением, то, очевидноwrite(member)
, больше не будет писать число, которое выrandom()
редактировали. Это буквально просто здравый смысл. Точно так же, если вы это сделаетеF(){ F(); }
, то удачи в вашем бесконечном цикле! Это верно везде и не является специфическим дляThreadLocal
.когда?
Когда объект не является потокобезопасным, вместо синхронизации, которая препятствует масштабируемости, передайте по одному объекту каждому потоку и оставьте его область видимости потока, то есть ThreadLocal. Одним из наиболее часто используемых, но не поточно-ориентированных объектов являются база данных Connection и JMSConnection.
Как ?
Одним из примеров является среда Spring, в которой ThreadLocal активно используется для скрытого управления транзакциями путем сохранения этих объектов подключения в переменных ThreadLocal. На высоком уровне, когда транзакция запускается, она получает соединение (и отключает автоматическую фиксацию) и сохраняет его в ThreadLocal. при дальнейших вызовах БД он использует то же соединение для связи с БД. В конце он берет соединение от ThreadLocal и фиксирует (или откатывает) транзакцию и освобождает соединение.
Я думаю, что log4j также использует ThreadLocal для поддержки MDC.
источник
ThreadLocal
полезно, когда вы хотите иметь какое-то состояние, которое не должно быть общим для разных потоков, но оно должно быть доступно каждому потоку в течение всего его срока службы.В качестве примера представим веб-приложение, в котором каждый запрос обслуживается отдельным потоком. Представьте, что для каждого запроса вам нужно многократно обрабатывать один фрагмент данных, что довольно дорого для вычисления. Однако эти данные могли изменяться для каждого входящего запроса, что означает, что вы не можете использовать простой кэш. Простым и быстрым решением этой проблемы было бы иметь
ThreadLocal
переменную, содержащую доступ к этим данным, так что вам придется рассчитывать ее только один раз для каждого запроса. Конечно, эту проблему также можно решить без использованияThreadLocal
, но я придумал ее для наглядности.Тем не менее, имейте в виду, что
ThreadLocal
это по существу форма глобального государства. В результате он имеет много других последствий и должен использоваться только после рассмотрения всех других возможных решений.источник
ThreadLocal
качестве поля объекта ...ThreadLocal
, где это было доступно во всем мире. Как вы сказали, он все еще может использоваться как поле класса, ограничивающее видимость.Здесь нет ничего нового, но сегодня я обнаружил, что
ThreadLocal
это очень полезно при использовании Bean Validation в веб-приложении. Сообщения проверки локализованы, но по умолчанию используютсяLocale.getDefault()
. Вы можете настроитьValidator
с другимMessageInterpolator
, но нет способа указать,Locale
когда вы звонитеvalidate
. Таким образом, вы можете создать статическийThreadLocal<Locale>
(или, еще лучше, общий контейнер с другими вещами, которые вам могут понадобиться),ThreadLocal
а затем сделать свойMessageInterpolator
выборLocale
из этого. Следующий шаг - написать объект,ServletFilter
который использует значение сеанса илиrequest.getLocale()
выбрать локаль и сохранить это в вашейThreadLocal
ссылке.источник
Как уже упоминалось @unknown (google), он используется для определения глобальной переменной, в которой указанное значение может быть уникальным в каждом потоке. Обычно его использование влечет за собой хранение некоторой контекстной информации, которая связана с текущим потоком выполнения.
Мы используем его в среде Java EE для передачи идентификатора пользователя классам, которые не поддерживают Java EE (не имеют доступа к HttpSession или EJB SessionContext). Таким образом, код, который использует идентификатор для операций, основанных на безопасности, может получить доступ к идентификатору из любого места, без необходимости явно передавать его при каждом вызове метода.
Цикл операций запрос / ответ в большинстве вызовов Java EE упрощает этот тип использования, поскольку он дает четко определенные точки входа и выхода для установки и сброса ThreadLocal.
источник
По определению:
Каждый
Thread
в Java содержитсяThreadLocalMap
в нем.куда
Достижение ThreadLocal:
Теперь создайте класс-оболочку для ThreadLocal, который будет содержать изменяемый объект, как показано ниже (с или без
initialValue()
).Теперь getter и setter этой оболочки будут работать с экземпляром threadlocal вместо изменяемого объекта.
Если getter () из threadlocal не нашел никакого значения с в threadlocalmap из
Thread
; тогда он вызовет initialValue (), чтобы получить свою частную копию относительно потока.Теперь
wrapper.getDateFormatter()
вызовемthreadlocal.get()
и проверим,currentThread.threadLocalMap
содержит ли это (threadlocal) экземпляр.Если да, вернуть значение (SimpleDateFormat) для соответствующего
экземпляра локального потока, иначе добавьте карту с этим экземпляром локального потока, initialValue ().
При этом безопасность потока достигается на этом изменяемом классе; каждый поток работает со своим изменяемым экземпляром, но с тем же экземпляром ThreadLocal. Означает, что все потоки будут использовать один и тот же экземпляр ThreadLocal в качестве ключа, но другой экземпляр SimpleDateFormat в качестве значения.
https://github.com/skanagavelu/yt.tech/blob/master/src/ThreadLocalTest.java
источник
Локальные переменные потока часто используются для предотвращения совместного использования в проектах, основанных на изменчивых синглетонах или глобальных переменных.
Он может использоваться в таких случаях, как создание отдельного соединения JDBC для каждого потока, когда вы не используете пул соединений.
Когда вы вызываете getConnection, он возвращает соединение, связанное с этим потоком. То же самое можно сделать с другими свойствами, такими как dateformat, контекст транзакции, который вы не хотите разделять между потоками.
Вы могли бы также использовать локальные переменные для них, но эти ресурсы обычно занимают время при создании, поэтому вы не хотите создавать их снова и снова, когда выполняете с ними некоторую бизнес-логику. Тем не менее, значения ThreadLocal хранятся в самом объекте потока, и как только поток подвергается сборке мусора, эти значения также исчезают.
Эта ссылка очень хорошо объясняет использование ThreadLocal.
источник
Существует 3 сценария использования помощника класса, такого как SimpleDateFormat, в многопоточном коде, лучший из которых - использование ThreadLocal.
Сценарии
1- Использование объекта общего ресурса с помощью механизма блокировки или синхронизации , который замедляет работу приложения
2- Использование в качестве локального объекта внутри метода
В этом сценарии, если у нас есть 4 потока, каждый из которых вызывает метод 1000 раз, мы создаем
4000 объектов SimpleDateFormat и ожидаем, пока GC их сотрет.
3- Использование ThreadLocal
если у нас есть 4 потока, и мы дали каждому потоку один экземпляр SimpleDateFormat,
то есть у нас 4 потока , 4 объекта SimpleDateFormat.
Нет необходимости в механизме блокировки, создании и уничтожении объектов. (Хорошая временная сложность и пространственная сложность)
источник
Класс ThreadLocal в Java позволяет создавать переменные, которые могут быть прочитаны и записаны только одним потоком. Таким образом, даже если два потока выполняют один и тот же код, а код имеет ссылку на переменную ThreadLocal, эти два потока не могут видеть переменные ThreadLocal друг друга.
Читать далее
источник
[Для справки] ThreadLocal не может решить проблемы обновления общего объекта. Рекомендуется использовать объект staticThreadLocal, который используется всеми операциями в одном потоке. [Обязательный] метод remove () должен быть реализован переменными ThreadLocal, особенно при использовании пулов потоков, в которых потоки часто используются повторно. В противном случае это может повлиять на последующую бизнес-логику и вызвать непредвиденные проблемы, такие как утечка памяти.
источник
Кешируя, иногда вам приходится вычислять одно и то же значение много времени, поэтому, сохраняя последний набор входных данных для метода и результата, вы можете ускорить код. Используя Thread Local Storage, вам не нужно думать о блокировке.
источник
ThreadLocal - это специально предоставляемая функциональность JVM для предоставления изолированного пространства хранения только для потоков. подобно значению переменной области видимости, привязанной только к данному экземпляру класса. каждый объект имеет свои единственные значения, и они не могут видеть значения друг друга. как и концепция переменных ThreadLocal, они являются локальными для потока в том смысле, что экземпляры объектов, которые другие потоки, кроме той, которая его создала, не могут ее видеть. Посмотреть здесь
источник
Threadlocal обеспечивает очень простой способ достижения повторного использования объектов с нулевой стоимостью.
У меня была ситуация, когда несколько потоков создавали образ изменяемого кэша при каждом уведомлении об обновлении.
Я использовал Threadlocal в каждом потоке, а затем каждому потоку нужно было просто сбросить старое изображение и затем обновлять его из кэша при каждом уведомлении об обновлении.
Обычные повторно используемые объекты из пулов объектов связаны с затратами на безопасность потоков, в то время как в этом подходе их нет.
источник
Попробуйте этот небольшой пример, чтобы почувствовать переменную ThreadLocal:
Вывод на консоль, если поток BookA выполняется первым:
Результат BookA: 'wordA1, wordA2, wordA3'.
Результат BookB: «wordB1, wordB2».
Вывод на консоль, если сначала выполняется поток BookB:
Результат BookB: 'wordB1, wordB2'.
Результат BookA: «wordA1, wordA2, wordA3».
источник