Эта статья участвовала в третьем этапе тренировочного лагеря для создателей Nuggets. Подробности см.:Dig Li Project | Идет третий этап тренировочного лагеря создателя, «написание» личного влияния.
📖Предисловие
Сегодня блогер поделится с вами Java (обязательно для интервью): в интервьюThreadLocal
Принципы и сценарии использования, если не нравится - не распыляйте, если есть возражения, добро пожаловать к обсуждению!
Я думаю, вас часто спрашивают в интервьюThreadLocal
Хотя я всегда знал о существовании этой вещи, я плохо изучил принцип, и у меня нет собственной системы знаний. Сегодня у Amway волна улучшений
🚀ThreadLocal
что
ThreadLocal
даJDK java.lang
Инструмент в пакете для реализации одних и тех же данных потока, совместно использующих различную изоляцию данных потока. Посмотрим, как это объясняется в исходном коде JDK: после перевода
ThreadLocal
Этот класс предоставляет локальные переменные потока, которые отличаются от других обычных переменных тем, что каждый поток, обращающийся к переменной, имеет внутри себя отдельную инициализированную копию переменной;ThreadLocal
Переменные экземпляра обычно принимаютprivate static
Украсьте в классе.если только
ThreadLocal
К переменным можно получить доступ, и поток жив, тогда каждый поток будет содержатьThreadLocal
копия переменной. Когда поток заканчивается, всеThreadLocal
Копии относительных экземпляров могут быть переработаны.
Одним словом этоThreadLocal
Применимо к каждому потоку, которому нужен собственный независимый экземпляр, и экземпляр должен использоваться в нескольких методах (совместное использование данных одного и того же потока), то есть переменные изолированы между потоками (разная изоляция данных потока) и совместно используются методами или классами. Сцены.
✨ThreadLocal
использовать
package com.test;
import java.util.concurrent.CountDownLatch;
/**
*
* @Description: 我的测试类
* @ClassName: MyDemo.java
* @author ChenYongJia
* @Date 2019年4月17日 晚上23:25
* @Email chen87647213@163.com
*/
public class MyDemo {
private String string;
private String getString() {
return string;
}
private void setString(String string) {
this.string = string;
}
public static void main(String[] args) {
int threads = 9;
MyDemo demo = new MyDemo();
CountDownLatch countDownLatch = new CountDownLatch(threads);
for (int i = 0; i < threads; i++) {
Thread thread = new Thread(() -> {
demo.setString(Thread.currentThread().getName());
System.out.println("demo.getString()================>" + demo.getString());
countDownLatch.countDown();
}, "执行线程 - " + i);
thread.start();
}
}
}
консольный вывод
demo.getString()================>执行线程 - 0
demo.getString()================>执行线程 - 1
demo.getString()================>执行线程 - 2
demo.getString()================>执行线程 - 3
demo.getString()================>执行线程 - 4
demo.getString()================>执行线程 - 6
demo.getString()================>执行线程 - 7
demo.getString()================>执行线程 - 5
demo.getString()================>执行线程 - 8
Из результатов видно, что исключение возникает, когда несколько потоков обращаются к одной и той же переменной, а данные между потоками не изолированы. Давайте посмотрим на следующееThreadLocal
Пример вариативного способа решения этой задачи.
package com.test;
import java.util.concurrent.CountDownLatch;
/**
*
* @Description: 我的测试类
* @ClassName: MyThreadLocalDemo.java
* @author ChenYongJia
* @Date 2019年4月17日 晚上23:28
* @Email chen87647213@163.com
*/
public class MyThreadLocalDemo {
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
private String getString() {
return threadLocal.get();
}
private void setString(String string) {
threadLocal.set(string);
}
public static void main(String[] args) {
int threads = 9;
MyThreadLocalDemo demo = new MyThreadLocalDemo();
CountDownLatch countDownLatch = new CountDownLatch(threads);
for (int i = 0; i < threads; i++) {
Thread thread = new Thread(() -> {
demo.setString(Thread.currentThread().getName());
System.out.println("demo.getString()================>" + demo.getString());
countDownLatch.countDown();
}, "执行线程 - " + i);
thread.start();
}
}
}
консольный вывод
demo.getString()================>执行线程 - 0
demo.getString()================>执行线程 - 1
demo.getString()================>执行线程 - 4
demo.getString()================>执行线程 - 3
demo.getString()================>执行线程 - 2
demo.getString()================>执行线程 - 7
demo.getString()================>执行线程 - 5
demo.getString()================>执行线程 - 8
demo.getString()================>执行线程 - 6
Судя по результатам, очень удобно решать проблему изоляции данных между несколькими потоками.
Некоторые друзья здесь могут подумать, что в примере 1 мы можем реализовать эту функцию с помощью блокировки.Да, блокировка действительно может решить эту проблему, но здесь мы подчеркиваем проблему изоляции потоков данных, а не проблему многопоточного совместного использования данных. Если мы здесь, кромеgetString()
Есть много других способов использовать этоString
, в настоящее время нет явного процесса передачи данных между различными методами, и они могут быть напрямуюThreadLocal
переменная, этоThreadLocal
Ядро одного и того же потока данных разделяет другую изоляцию данных потока.
так какThreadLocal
для поддержки дженериков, здесь для храненияString
Чтобы продемонстрировать, на самом деле можно сохранить любой тип, и эффект тот же.
🐱🏍ThreadLocal
Анализ исходного кода
Прежде чем анализировать исходный код, мы понимаем одну вещь, что экземпляр объекта иThreadLocal
Отношение отображения переменных определяется потокомThread
Для обслуживания экземпляр объекта иThreadLocal
Отношение отображения переменных определяется потокомThread
Для обслуживания экземпляр объекта иThreadLocal
Отношение отображения переменных определяется потокомThread
поддерживать. Важные вещи говорятся трижды.
Фактически экземпляр объекта иThreadLocal
Отношение отображения переменных хранится вMap
внутри (этоMap
является абстрактнымMap
нетjava.util
серединаMap
), при этомMap
даThread
Поле класс! И реальные отношения сопоставления хранилищаMap
то естьThreadLocalMap
. Давайте посмотрим на конкретную реализацию через несколько методов в исходном коде.
//set 方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
//获取线程中的ThreadLocalMap 字段!!
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
//创建线程的变量
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
существуетset
Метод сначала получает текущий поток, а затем передаетgetMap
Получить текущий потокThreadLocalMap
переменная типаthreadLocals
, если он существует, он будет назначен напрямую, если он не существует, он будет создан для потокаThreadLocalMap
переменные и присваивать значения. при назначенииthis
это сам экземпляр объекта, который вызывает переменную.
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();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
get
Этот метод также относительно прост, и он также сначала получает текущий поток.ThreadLocalMap
Переменная, которая возвращает значение, если оно существует, или создает и возвращает начальное значение, если оно не существует.
🎶ThreadLocalMap
Анализ исходного кода
ThreadLocal
Базовая реализация осуществляется черезThreadLocalMap
Чтобы достичь этого, давайте сначала посмотримThreadLocalMap
определение, а затем посмотрите на соответствующееset
а такжеget
метод.
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;
}
}
/**
* The table, resized as necessary.
* table.length MUST always be a power of two.
*/
private Entry[] table;
}
ThreadLocalMap
используется вEntry[]
Массивы используются для хранения отношений между экземплярами объектов и переменными, а объекты-экземпляры используются какkey
, переменная какvalue
Осознайте соответствующие отношения. и вотkey
Используются слабые ссылки на объекты-экземпляры (поскольку нашаkey
Это экземпляр объекта. Каждый экземпляр объекта имеет свой жизненный цикл. Здесь можно использовать слабую ссылку для ссылки на него, не влияя на жизненный цикл экземпляра объекта).
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
//获取 hash 值,用于数组中的下标
int i = key.threadLocalHashCode & (len-1);
//如果数组该位置有对象则进入
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
//k 相等则覆盖旧值
if (k == key) {
e.value = value;
return;
}
//此时说明此处 Entry 的 k 中的对象实例已经被回收了,需要替换掉这个位置的 key 和 value
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
//创建 Entry 对象
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
//获取 Entry
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
закончить смотретьThreadLocal
СвязанныйJDK源码
, У меня самого есть определенное понимание, и я надеюсь, что это может помочь всем.*
Иди сюда Java (требуется для собеседований): На собеседованииThreadLocal
Принцип и сценарии использования поделились, пробуйте!
🎉 Наконец-то
-
Для обработки транзакций на сервисном уровне вы можете использовать это для сохранения объекта Connection.
-
Дополнительные справочные сообщения в блоге см. здесь:"Блог Чен Юнцзя"
-
Друзья, которым нравятся блогеры, могут подписаться, поставить лайк и продолжать обновлять, хе-хе!