Когда и как я должен использовать переменную ThreadLocal?
Когда я должен использовать ThreadLocal
переменная?
Как это используется?
26 ответов
Одно из возможных (и распространенных) применений - когда у вас есть какой-то объект, который не является потокобезопасным, но вы хотите избежать синхронизации доступа к этому объекту (я смотрю на вас, SimpleDateFormat). Вместо этого дайте каждому потоку свой экземпляр объекта.
Например:
public class Foo
{
// SimpleDateFormat is not thread-safe, so give one to each thread
private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue()
{
return new SimpleDateFormat("yyyyMMdd HHmm");
}
};
public String formatIt(Date date)
{
return formatter.get().format(date);
}
}
Так как ThreadLocal
ссылка на данные в данном Thread
, вы можете закончить с утечкой загрузки классов при использовании ThreadLocal
в серверах приложений, которые используют пулы потоков. Вы должны быть очень осторожны при очистке любого ThreadLocal
с тобой get()
или же set()
используя ThreadLocal
"s remove()
метод.
Если вы не выполните очистку, когда закончите, все ссылки на классы, загруженные как часть развернутого веб-приложения, останутся в постоянной куче и никогда не будут собирать мусор. Повторное развертывание / удаление веб-приложения не очистит каждый 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-эквивалента глобальной переменной. В многопоточном слове эквивалентом глобальной переменной является локальная переменная потока.
В книге Java Concurrency in Practice есть очень хороший пример. Где автор ( Джошуа Блох) объясняет, как ограничение потоков является одним из самых простых способов обеспечения безопасности потоков, а ThreadLocal является более формальным средством поддержания ограничения потоков. В конце он также объясняет, как люди могут злоупотреблять им, используя его как глобальные переменные.
Я скопировал текст из упомянутой книги, но код 3.10 отсутствует, так как не так важно понимать, где следует использовать ThreadLocal.
Локальные переменные потока часто используются для предотвращения совместного использования в проектах, основанных на изменчивых синглетонах или глобальных переменных. Например, однопоточное приложение может поддерживать глобальное соединение с базой данных, которое инициализируется при запуске, чтобы избежать необходимости передавать Соединение каждому методу. Поскольку соединения JDBC могут быть не поточно-ориентированными, многопоточное приложение, использующее глобальное соединение без дополнительной координации, также не является поточно-ориентированным. Используя ThreadLocal для хранения соединения JDBC, как в ConnectionHolder в Листинге 3.10, каждый поток будет иметь свое собственное соединение.
ThreadLocal широко используется при реализации каркасов приложений. Например, контейнеры J2EE связывают контекст транзакции с исполняющим потоком на время вызова EJB. Это легко реализовать с помощью статического Thread-Local, содержащего контекст транзакции: когда код платформы должен определить, какая транзакция выполняется в данный момент, он выбирает контекст транзакции из этого ThreadLocal. Это удобно тем, что уменьшает необходимость передавать информацию о контексте выполнения в каждый метод, но связывает любой код, который использует этот механизм, с платформой.
Легко злоупотреблять 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.
Подробнее: http://javarevisited.blogspot.com/2012/05/how-to-use-threadlocal-in-java-benefits.html
В документации очень хорошо сказано: "каждый поток, который обращается к [локальной переменной потока] (через свой метод get или set), имеет свою собственную, независимо инициализированную копию переменной".
Вы используете один, когда каждый поток должен иметь свою собственную копию чего-либо. По умолчанию данные распределяются между потоками.
Сервер Webapp может хранить пул потоков и ThreadLocal
var должен быть удален до ответа клиенту, поэтому текущий поток может быть повторно использован при следующем запросе.
Два варианта использования, где можно использовать переменную threadlocal:
1- Когда у нас есть требование связать состояние с потоком (например, идентификатор пользователя или идентификатор транзакции). Это обычно происходит с веб-приложением, когда каждый запрос к сервлету имеет уникальный идентификатор транзакции, связанный с ним.
// This class will provide a thread local variable which
// will provide a unique ID for each thread
class ThreadId {
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId =
ThreadLocal.<Integer>withInitial(()-> {return nextId.getAndIncrement();});
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
}
Обратите внимание, что здесь метод withInitial реализован с использованием лямбда-выражения.
2- Другой случай использования - когда мы хотим иметь потокобезопасный экземпляр, и мы не хотим использовать синхронизацию, поскольку затраты на производительность с синхронизацией больше. Одним из таких случаев является использование SimpleDateFormat. Так как SimpleDateFormat не является потокобезопасным, поэтому мы должны предоставить механизм, чтобы сделать его потокобезопасным.
public class ThreadLocalDemo1 implements Runnable {
// threadlocal variable is created
private static final ThreadLocal<SimpleDateFormat> dateFormat = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue(){
System.out.println("Initializing SimpleDateFormat for - " + Thread.currentThread().getName() );
return new SimpleDateFormat("dd/MM/yyyy");
}
};
public static void main(String[] args) {
ThreadLocalDemo1 td = new ThreadLocalDemo1();
// Two threads are created
Thread t1 = new Thread(td, "Thread-1");
Thread t2 = new Thread(td, "Thread-2");
t1.start();
t2.start();
}
@Override
public void 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(new Date()));
}
}
Начиная с выпуска Java 8, существует более декларативный способ инициализации ThreadLocal
:
ThreadLocal<Cipher> local = ThreadLocal.withInitial(() -> "init value");
До выхода Java 8 вам приходилось делать следующее:
ThreadLocal<String> local = new ThreadLocal<String>(){
@Override
protected String initialValue() {
return "init value";
}
};
Более того, если метод создания экземпляра (конструктор, метод фабрики) класса, который используется для ThreadLocal
не принимает никаких параметров, вы можете просто использовать ссылки на методы (представленные в Java 8):
class NotThreadSafe {
// no parameters
public NotThreadSafe(){}
}
ThreadLocal<NotThreadSafe> container = ThreadLocal.withInitial(NotThreadSafe::new);
Примечание: оценка ленива, так как вы проходите java.util.function.Supplier
лямбда, которая оценивается только когда ThreadLocal#get
вызывается, но значение не было предварительно оценено.
Существует 3 сценария использования помощника класса, такого как SimpleDateFormat, в многопоточном коде, лучший из которых - использование ThreadLocal.
Сценарии
1- Использование объекта общего ресурса с помощью механизма блокировки или синхронизации, который замедляет работу приложения
2- Использование в качестве локального объекта внутри метода
В этом случае, если у нас есть 4 потока, каждый из которых вызывает метод 1000 раз, то мы имеем
4000 SimpleDateFormat объект создан и ждет GC, чтобы стереть их
3- Использование ThreadLocal
если у нас есть 4 потока, и мы дали каждому потоку один экземпляр SimpleDateFormat
Итак, у нас есть 4 потока, 4 объекта SimpleDateFormat.
Нет необходимости в механизме блокировки, создании и уничтожении объектов. (Хорошее время сложность и сложность пространства)
Вы должны быть очень осторожны с паттерном ThreadLocal. Есть несколько основных недостатков, как упомянул Фил, но один из них не был упомянут, чтобы убедиться, что код, который устанавливает контекст ThreadLocal, не является "повторным".
Плохие вещи могут случиться, когда код, который устанавливает информацию, запускается второй или третий раз, потому что информация в вашем потоке может начать изменяться, когда вы этого не ожидаете. Поэтому позаботьтесь о том, чтобы информация о ThreadLocal не была установлена до того, как вы установите ее снова.
ThreadLocal обеспечит синхронизацию доступа к изменяемому объекту несколькими потоками в несинхронизированном методе, что означает, что изменяемый объект будет неизменным внутри метода.
Это достигается путем предоставления нового экземпляра изменяемого объекта для каждого потока, пытающегося получить к нему доступ. Так что это локальная копия каждого потока. Это некоторый хак при создании переменной экземпляра в методе, к которому нужно обращаться как в локальной переменной. Как вы знаете, локальная переменная метода доступна только для потока, есть одно отличие; Локальные переменные метода не будут доступны для потока после завершения выполнения метода, поскольку изменяемый объект, совместно используемый с threadlocal, будет доступен для нескольких методов, пока мы его не очистим.
По определению:
Класс ThreadLocal в Java позволяет создавать переменные, которые могут быть прочитаны и записаны только одним потоком. Таким образом, даже если два потока выполняют один и тот же код, а код имеет ссылку на переменную ThreadLocal, эти два потока не могут видеть переменные ThreadLocal друг друга.
каждый Thread
в яве содержится ThreadLocalMap
в этом.
куда
Key = One ThreadLocal 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(), чтобы получить свою личную копию по отношению к потоку.
class SimpleDateFormatInstancePerThread {
private static final ThreadLocal<SimpleDateFormat> dateFormatHolder = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd") {
UUID id = UUID.randomUUID();
@Override
public String 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
*/
public static DateFormat getDateFormatter() {
return dateFormatHolder.get();
}
public static void cleanup() {
dateFormatHolder.remove();
}
}
Сейчас wrapper.getDateFormatter()
позвоню threadlocal.get()
и это проверит currentThread.threadLocalMap
содержит этот (threadlocal) экземпляр
Если да, вернуть значение (SimpleDateFormat) для соответствующего экземпляра локального потока
иначе добавьте карту с этим локальным экземпляром thread, initialValue().
При этом безопасность потока достигается на этом изменяемом классе; каждый поток работает со своим изменяемым экземпляром, но с тем же экземпляром ThreadLocal. Означает, что все потоки будут использовать один и тот же экземпляр ThreadLocal в качестве ключа, но разные экземпляры SimpleDateFormat в качестве значения.
https://github.com/skanagavelu/yt.tech/blob/master/src/ThreadLocalTest.java
ThreadLocal
полезно, когда вы хотите иметь какое-то состояние, которое не должно быть общим для разных потоков, но оно должно быть доступно из каждого потока в течение всего его срока службы.
В качестве примера представим веб-приложение, в котором каждый запрос обслуживается отдельным потоком. Представьте себе, что для каждого запроса вам нужно многократно использовать один фрагмент данных, что довольно дорого для вычисления. Однако эти данные могли изменяться для каждого входящего запроса, что означает, что вы не можете использовать простой кэш. Простое, быстрое решение этой проблемы было бы иметь ThreadLocal
переменная, хранящая доступ к этим данным, так что вам придется рассчитывать ее только один раз для каждого запроса. Конечно, эта проблема также может быть решена без использования ThreadLocal
, но я придумал это для наглядности.
Тем не менее, имейте в виду, что ThreadLocal
по сути, это форма глобального государства. В результате он имеет много других последствий и должен использоваться только после рассмотрения всех других возможных решений.
Когда?
Когда объект не является потокобезопасным, вместо синхронизации, которая препятствует масштабируемости, передайте по одному объекту каждому потоку и оставьте его область видимости потока, то есть ThreadLocal. Одним из наиболее часто используемых, но не поточно-ориентированных объектов являются база данных Connection и JMSConnection.
Как?
Одним из примеров является Spring Framework, который активно использует ThreadLocal для управления транзакциями за кулисами, сохраняя эти объекты подключения в переменных ThreadLocal. На высоком уровне, когда транзакция запускается, она получает соединение (и отключает автоматическую фиксацию) и сохраняет его в ThreadLocal. при последующих вызовах БД он использует то же соединение для связи с БД. В конце он берет соединение от ThreadLocal и фиксирует (или откатывает) транзакцию и освобождает соединение.
Я думаю, что log4j также использует ThreadLocal для поддержки MDC.
Здесь нет ничего нового, но сегодня я обнаружил, что 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.
Локальные переменные потока часто используются для предотвращения совместного использования в проектах, основанных на изменчивых синглетонах или глобальных переменных.
Он может использоваться в таких случаях, как создание отдельного соединения JDBC для каждого потока, когда вы не используете пул соединений.
private static ThreadLocal<Connection> connectionHolder
= new ThreadLocal<Connection>() {
public Connection initialValue() {
return DriverManager.getConnection(DB_URL);
}
};
public static Connection getConnection() {
return connectionHolder.get();
}
Когда вы вызываете getConnection, он возвращает соединение, связанное с этим потоком. То же самое можно сделать с другими свойствами, такими как dateformat, контекст транзакции, который вы не хотите разделять между потоками.
Вы могли бы также использовать локальные переменные для них, но эти ресурсы обычно занимают время при создании, поэтому вы не хотите создавать их снова и снова, когда выполняете с ними некоторую бизнес-логику. Тем не менее, значения ThreadLocal хранятся в самом объекте потока, и как только поток подвергается сборке мусора, эти значения также исчезают.
Эта ссылка очень хорошо объясняет использование ThreadLocal.
[Для справки]ThreadLocal не может решить проблемы обновления общего объекта. Рекомендуется использовать объект staticThreadLocal, который используется всеми операциями в одном потоке. [Обязательный] метод remove() должен быть реализован переменными ThreadLocal, особенно при использовании пулов потоков, в которых потоки часто используются повторно. В противном случае это может повлиять на последующую бизнес-логику и вызвать непредвиденные проблемы, такие как утечка памяти.
Кешируя, иногда вам приходится вычислять одно и то же значение много времени, поэтому, сохраняя последний набор входных данных для метода и результата, вы можете ускорить код. Используя Thread Local Storage, вам не нужно думать о блокировке.
ThreadLocal - это специально предоставляемая функциональность JVM для предоставления изолированного пространства хранения только для потоков. подобно значению переменной области видимости, привязанной только к данному экземпляру класса. каждый объект имеет свои единственные значения, и они не могут видеть значения друг друга. как и концепция переменных ThreadLocal, они являются локальными для потока в том смысле, что экземпляры объектов, которые другие потоки, кроме той, которая его создала, не могут ее видеть. Посмотреть здесь
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
public class ThreadId {
private static final AtomicInteger nextId = new AtomicInteger(1000);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId = ThreadLocal.withInitial(() -> nextId.getAndIncrement());
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
public static void main(String[] args) {
new Thread(() -> IntStream.range(1, 3).forEach(i -> {
System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
})).start();
new Thread(() -> IntStream.range(1, 3).forEach(i -> {
System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
})).start();
new Thread(() -> IntStream.range(1, 3).forEach(i -> {
System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
})).start();
}
}
Класс ThreadLocal в Java позволяет создавать переменные, которые могут быть прочитаны и записаны только одним потоком. Таким образом, даже если два потока выполняют один и тот же код, а код имеет ссылку на переменную ThreadLocal, эти два потока не могут видеть переменные ThreadLocal друг друга.
Попробуйте этот небольшой пример, чтобы почувствовать переменную ThreadLocal:
public class Book implements Runnable {
private static final ThreadLocal<List<String>> WORDS = ThreadLocal.withInitial(ArrayList::new);
private final String bookName; // It is also the thread's name
private final List<String> words;
public Book(String bookName, List<String> words) {
this.bookName = bookName;
this.words = Collections.unmodifiableList(words);
}
public void run() {
WORDS.get().addAll(words);
System.out.printf("Result %s: '%s'.%n", bookName, String.join(", ", WORDS.get()));
}
public static void main(String[] args) {
Thread t1 = new Thread(new Book("BookA", Arrays.asList("wordA1", "wordA2", "wordA3")));
Thread t2 = new Thread(new Book("BookB", Arrays.asList("wordB1", "wordB2")));
t1.start();
t2.start();
}
}
Вывод в консоль, если поток BookA выполняется первым:
Результат BookA: 'wordA1, wordA2, wordA3'.
Результат BookB: 'wordB1, wordB2'.
Вывод в консоль, если поток BookB выполняется первым:
Результат BookB: 'wordB1, wordB2'.
Результат BookA: 'wordA1, wordA2, wordA3'.
Threadlocal обеспечивает очень простой способ достижения повторного использования объектов с нулевой стоимостью.
У меня была ситуация, когда несколько потоков создавали образ изменяемого кэша при каждом уведомлении об обновлении.
Я использовал Threadlocal в каждом потоке, а затем каждому потоку нужно было просто сбросить старое изображение и затем обновлять его из кэша при каждом уведомлении об обновлении.
Обычные повторно используемые объекты из пулов объектов связаны с затратами на безопасность потоков, в то время как в этом подходе их нет.
1-й вариант использования - контекст для каждого потока, который обеспечивает безопасность потоков, а также производительность Пример в реальном времени в классах SpringFramework -
- LocaleContextHolder
- TransactionContextHolder
- RequestContextHolder
- DateTimeContextHolder
2-й вариант использования - когда мы не хотим делиться чем-то между потоками и в то же время не хотим использовать синхронизацию / блокировку из-за примера стоимости производительности - SimpleDateFormat для создания настраиваемого формата для дат
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author - GreenLearner(https://www.youtube.com/c/greenlearner)
*/
public class ThreadLocalDemo1 {
SimpleDateFormat sdf = new SimpleDateFormat("dd-mm-yyyy");//not thread safe
ThreadLocal<SimpleDateFormat> tdl1 = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-dd-mm"));
public static void main(String[] args) {
ThreadLocalDemo1 d1 = new ThreadLocalDemo1();
ExecutorService es = Executors.newFixedThreadPool(10);
for(int i=0; i<100; i++) {
es.submit(() -> System.out.println(d1.getDate(new Date())));
}
es.shutdown();
}
String getDate(Date date){
// String s = tsdf.get().format(date);
String s1 = tdl1.get().format(date);
return s1;
}
}
Советы по использованию
- Если возможно, используйте локальные переменные. Таким образом мы можем избежать использования ThreadLocal
- Делегируйте функциональность фреймворкам по мере возможности
- Если вы используете ThreadLocal и устанавливаете в нем состояние, обязательно очистите его после использования, иначе это может стать основной причиной OutOfMemoryError