Что даёт переменная типа ThreadLocal?
- Где используют ThreadLocal?
- Почему ThreadLocal опасен в пуле потоков?
ThreadLocal — своя ячейка значения для каждого потока: синхронизация не нужна, потому что значение никто не разделяет.
- Применение: контекст запроса, пользователь, транзакция, непотокобезопасный форматтер
- В пуле потоки переиспользуются: не очищенное значение достанется следующему запросу и утечёт память
А ещё Всегда remove() в finally; для виртуальных потоков есть ScopedValue
Подробно
ThreadLocal<T> хранит отдельное значение для каждого потока: set() и get() работают с ячейкой текущего потока, другие потоки её не видят. Раз значение не разделяется, синхронизация не нужна.
Где используют:
- контекст обработки запроса — id запроса для логов, текущий пользователь (так устроены
SecurityContextHolderв Spring Security и MDC в логировании); - привязка транзакции или соединения к потоку;
- непотокобезопасные объекты, которые дорого создавать, — например, старый
SimpleDateFormat.
Главная опасность — пулы потоков. Поток в пуле живёт долго и обслуживает много задач. Если не очистить значение, следующая задача увидит чужой контекст (данные другого пользователя!), а объекты не будут собраны сборщиком — утечка памяти. Поэтому шаблон: set в начале обработки, remove() в finally.
С виртуальными потоками (Java 21) их могут быть миллионы, и ThreadLocal становится дорогим по памяти; для передачи контекста появился ScopedValue.
Пример
private static final ThreadLocal<String> REQUEST_ID = new ThreadLocal<>();
void handle(Request req) {
REQUEST_ID.set(req.id());
try {
process(req);
} finally {
REQUEST_ID.remove(); // обязательно в пуле потоков
}
}Что хотят услышать
- Отдельное значение на поток, без синхронизации.
- Примеры применения.
- Проблема пулов: утечки и чужой контекст, remove в finally.
Частые ошибки
- Не очищать значение после обработки в пуле потоков.
- Рассчитывать, что значение перейдёт в порождённые задачи.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✓Своё значение у каждого потока
- ✗Общее значение, синхронизированное между всеми потоками
- ✗Копию переменной, обновляемую при каждом переключении контекста
- ✗Значение, доступное только главному потоку
Что спросят дальше
- Почему это опасно при работе с пулом потоков?
- Что происходит с таким значением при переходе в другой поток?
Разбор темы
- ThreadLocal и Fork/Join
Состояние на поток и утечки в пулах, InheritableThreadLocal, work-stealing в ForkJoinPool и общий пул parallel streams.
Связанные вопросы
- В каких случаях parallel stream, скорее всего, не даст выигрыша или окажется медленнее последовательного? Middle
- В parallel stream выполняется обращение к внешнему HTTP-сервису. Почему это опасно для всего приложения? Middle
- Что такое Fork/Join framework и work stealing? Senior
- В чём опасность ThreadLocal в пуле потоков? Middle
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.