Сегодня вы узнаете о ThreadLocal — одном из распространенных классов, используемых при разработке Java-приложений.![Кофе-брейк #160. Глубокое погружение в Java ThreadLocal. Класс Scanner в Java - 1]()
![Кофе-брейк #160. Глубокое погружение в Java ThreadLocal. Класс Scanner в Java - 2]()
![Кофе-брейк #160. Глубокое погружение в Java ThreadLocal. Класс Scanner в Java - 3]()
![Кофе-брейк #160. Глубокое погружение в Java ThreadLocal. Класс Scanner в Java - 4]()
Что такое ThreadLocal?
Класс ThreadLocal хранит локальные переменные для потоков. Эти переменные изолированы между разными потоками и могут быть доступны только своему собственному потоку. Варианты применения ThreadLocal:- Изоляция данных между потоками.
- Управление сессиями для подключения к базе данных.
- Хранение транзакционной информации потока.

Как использовать ThreadLocal?
Давайте рассмотрим простой пример.
public static void main(String[] args) {
//Создаем ThreadLocal
ThreadLocal<String> local = new ThreadLocal<>();
//Создаем новый класс Random
Random random = new Random();
//Создаем 5 потоков
IntStream.range(0, 5).forEach(a-> new Thread(()-> {
//Присваиваем значение каждому потоку
local.set(a+" "+random.nextInt(100));
System.out.println("Thread number and its local value "+ local.get());
}).start());
}
В приведенном выше коде мы создаем класс ThreadLocal, создаем 5 потоков, присваиваем значение ThreadLocal в каждом потоке и выводим на печать. При выводе получаем:

Что под капотом?
Если посмотреть внимательно, то можно понять, что в ThreadLocal из этого примера кода есть два важных метода.public T get() {}
public void set (T value) {}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
Метод setter сначала получает текущий поток и вызывает метод getMap() для получения класса ThreadLocalMap. Если map существует, возьмите текущий поток t в качестве ключа, входной параметр в качестве значения и установите пару {key:value} в map. Если нет, то создайте map.
Теперь у вас может появиться вопрос — что такое ThreadLocalMap?
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}
ThreadLocalMap — это внутренний статический класс в ThreadLocal, определяющий класс Entry для хранения данных. Entry использует экземпляр ThreadLocal в качестве ключа и устанавливает значение, которое мы передаем.
Если на данном этапе это звучит слишком запутанно, просто запомните, что именно класс Entry в ThreadLocalMap выполняет фактическое хранение значений.
Чтобы получить данные из ThreadLocal, мы используем метод getter:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
В методе getter мы будем использовать currentThread в качестве ключа для получения ThreadLocalMap. Затем map получит getEntry() на основе экземпляра ThreadLocal и вернет экземпляр Entry, а затем сохраненное значение.
Вот схема, которая поможет разобраться:


Каждый поток поддерживает ссылку на ThreadLocalMap.
ThreadLocalMap является внутренним статическим классом ThreadLocal и использует класс Entry для хранения.
Ключ ThreadLocalMap является экземпляром ThreadLocal и может иметь несколько ThreadLocal.
Сам ThreadLocal не хранит значение, но это ключ для потока, который поможет получить значение из ThreadLocalMap.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ