Ставьте лайк и смотрите снова, формируйте привычку и ищите в WeChat [Ао Бинг] Обратите внимание на этого программиста, который изо всех сил пытается выжить в Интернете.
эта статьяGitHub github.com/JavaFamilyВключено, и есть полные тестовые площадки, материалы и мой цикл статей для интервью с производителями первой линии.
открывалка
В последнее время у Чжан Саня была очень жаркая погода, поэтому он решил пойти на собеседование и поболтать с интервьюером, чтобы решить эту проблему.В результате кто-то назначил встречу на собеседование, как только он отправил свое резюме.
Я потерял его, почему кто-то попросил меня об интервью сразу после того, как я его дал? Эх. . . Это действительно раздражает, брат так долго был вдали от Цзянху, у Цзянху до сих пор есть легенда о брате, я все еще так популярен? Слишком раздражающий, красивый и невинный.
Чжан Сан, который был тайно радоваться, пришел в офис определенного интервью на месте донга, я потерял его, этот интервьюер? Нет, это Mac, полный царапин, этот том, это легендарный архитектор?
Менталитет Чжан Сан рухнул внезапно, и он встретил лучший интервьюер в своем первом интервью. Кто мог бы выдержать?
Здравствуйте, я ваш интервьюер, Тони, вы сможете догадаться, кто я, по моей прическе, и я ничего не скажу, давайте начнем, хорошо? Увидев, что вы написали многопоточность в своем резюме, подойди и расскажи мне о ThreadLocal. Я давно не писал код и не знаком с ним. Пожалуйста, помогите мне его вспомнить.
Я потерял? Это ТМ человек? В чем логика этого, мол спросить про многопоточность и тут придумать такой непопулярный ThreadLocal? Твой менталитет сломан.Кроме того,ты сам забыл прочитать книгу.Какого черта ты пришел ко мне,чтобы найти ответ...
Хоть и очень неохотно, Чжан Сан все же прокручивал свою маленькую голову на высокой скорости, вспоминая различные детали ThreadLocal...
Интервьюер сказал правду, что я не очень часто использовал ThreadLocal в реальном процессе разработки.Когда я писал эту статью, я намеренно открывал десятки проектов на своем компьютере и искал ThreadLocal по всему миру.Редко используется в проектах, но есть еще какой-то.
Роль ThreadLocal в основном заключается в изоляции данных.Заполненные данные принадлежат только текущему потоку, а данные переменных относительно изолированы от других потоков.В многопоточной среде, как предотвратить подделку ваших переменных другими потоками потоки.
Можете ли вы сказать мне, в чем польза его изоляции и в каких сценариях он будет использоваться?
Это я сказал, что редко им пользуюсь, и спросил меня, неудобно было, ой ой ой, когда вспомнил, уровень изоляции транзакций.
Привет, интервьюер, на самом деле, первое, что пришло мне в голову, это исходный код Spring-реализации уровня изоляции транзакций, который обнаружила моя девушка, когда меня бросила девушка в колледже, и я плакал в одиночестве в библиотеке.
Spring использует метод Threadlocal, чтобы гарантировать, что операция базы данных в одном потоке использует одно и то же соединение с базой данных.В то же время этот метод может заставить бизнес-уровень воспринимать и управлять объектом соединения при использовании транзакции и может умело управляйте им на уровне распространения.Переключайтесь между несколькими конфигурациями транзакций, приостанавливайте и возобновляйте.
ThreadLocal используется в среде Spring для достижения этой изоляции, в основном вTransactionSynchronizationManager
В этом классе код выглядит следующим образом:
private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<>("Transaction synchronizations");
private static final ThreadLocal<String> currentTransactionName =
new NamedThreadLocal<>("Current transaction name");
……
Транзакции Spring в основном реализуются с помощью ThreadLocal и AOP. Я упомяну об этом здесь. Все знают, что собственная ссылка каждого потока сохраняется с помощью ThreadLocal. Я подробнее остановлюсь на деталях в главе Spring, тепло?
Помимо сцен, где в исходном коде используется ThreadLocal, есть ли у вас какие-либо сцены, использующие его сами? Как бы вы обычно его использовали?
Вот оно, бонусный предмет здесь, я действительно сталкивался с этим раньше, и наконец-то появился шанс установить B.
Некоторые интервьюеры, это я буду! ! !
Прежде чем мы вышли в интернет, мы обнаружили, что даты некоторых пользователей были неправильными.SimpleDataFormat
В то время мы использовали метод parse() класса SimpleDataFormat, внутри которого находится объект Calendar.Вызов метода parse() класса SimpleDataFormat сначала вызывает Calendar.clear(), а затем вызывает Calendar.add(), если поток вызывает add first(), а затем другой поток вызывает clear(), в это время время парсинга методом parse() неверно.
На самом деле решить эту проблему очень просто, пусть каждый поток новый свойSimpleDataFormat
Это нормально, но 1000 потоков - это новые 1000SimpleDataFormat
?
Итак, в то время мы использовали пул потоков плюс оболочку ThreadLocal.SimpleDataFormat
, а затем вызовите initialValue, чтобы каждый поток имелSimpleDataFormat
Копия, которая решает проблему безопасности потоков, а также повышает производительность.
Тот……
Кроме того, у меня все еще есть, не спешите спрашивать следующий, позвольте мне добавить некоторые моменты и отсрочить время интервью.
В моем проекте есть поток, который часто сталкивается с несколькими вызовами методов, и объект, который необходимо передать, то есть контекст (Context), который является состоянием, часто идентификатор пользователя, информация о задаче и т. д., будет быть переходным параметром передачи Проблема.
Используя аналогичный режим цепочки ответственности, очень проблематично добавлять параметр контекста в каждый метод, а иногда, если в цепочке вызовов есть сторонняя библиотека, которая не может изменить исходный код, параметры объекта не могут быть переданы, поэтому я использовал ThreadLocal для этого.Внесите изменения, поэтому вам нужно только установить параметры в ThreadLocal перед вызовом и получить их в другом месте.
before
void work(User user) {
getInfo(user);
checkInfo(user);
setSomeThing(user);
log(user);
}
then
void work(User user) {
try{
threadLocalUser.set(user);
// 他们内部 User u = threadLocalUser.get(); 就好了
getInfo();
checkInfo();
setSomeThing();
log();
} finally {
threadLocalUser.remove();
}
}
Я видел много сценариев, в которых изоляция данных, таких как файлы cookie и сеансы, реализуется через ThreadLocal.
Кстати, интервьюер позволил мне показать широту познаний.В Android класс Looper использует характеристики ThreadLocal, чтобы гарантировать наличие только одного объекта Looper на поток.
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
Интервьюер: Я потерял сознание, откуда этот парень знает столько сцен? Он же вытащил Android, не так ли?Сэр, ниже я проверю его принципы.
Хм, вы очень хорошо ответили, не могли бы вы рассказать мне принцип его базовой реализации?
Хороший интервьюер, позвольте мне сначала рассказать о его использовании:
ThreadLocal<String> localName = new ThreadLocal();
localName.set("张三");
String name = localName.get();
localName.remove();
На самом деле, использование очень простое.После входа потока инициализируется общий объект ThreadLocal.После этого, пока поток переходит к получению перед удалением, он может получить значение предыдущего набора.Обратите внимание, что я говорить о прежде, чем удалить.
Он может изолировать данные между потоками, поэтому другие потоки не могут получить значения других потоков, используя метод Get (), но есть способ сделать это, что я буду говорить позже.
Давайте сначала посмотрим на исходный код его набора:
public void set(T value) {
Thread t = Thread.currentThread();// 获取当前线程
ThreadLocalMap map = getMap(t);// 获取ThreadLocalMap对象
if (map != null) // 校验对象是否为空
map.set(this, value); // 不为空set
else
createMap(t, value); // 为空创建一个map对象
}
Вы можете обнаружить, что исходный код set очень прост, в основном ThreadLocalMap, на который нам нужно обратить внимание, а ThreadLocalMap получается из переменной с именем threadLocals текущего потока Thread.
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
public class Thread implements Runnable {
……
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
……
Здесь мы можем в основном найти правду об изоляции данных ThreadLocal.Каждый поток Thread поддерживает свою собственную переменную threadLocals, поэтому, когда каждый поток создает ThreadLocal, данные фактически существуют в переменной threadLocals своего собственного потока Thread.Нет никакого способа получить их. , таким образом достигая изоляции.
Какова основная структура ThreadLocalMap?
Интервьюер хорошо задал этот вопрос, и я внутренне выругался, нельзя ли просто сделать перерыв?
Чжан Сан ответил с улыбкой, так как есть Map, его структура данных на самом деле очень похожа на HashMap, но глядя на исходный код, можно обнаружить, что он не реализует интерфейс Map, а его Entry наследует WeakReference (weak ссылка), также я не видел следующего в HashMap, поэтому связанного списка нет.
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
……
}
Структура наверное такая:
Подожди, у меня два вопроса, ты можешь на них ответить?
Хорошо, интервьюер, которого вы сказали.
Зачем вам массив? Как решить конфликт хэшей без связанного списка?
Массивы используются, потому что в процессе разработки поток может иметь несколько TreadLocals для хранения различных типов объектов, но все они будут помещены в ThreadLocalMap вашего текущего потока, поэтому должен храниться массив.
Что касается конфликтов хэшей, давайте сначала посмотрим на исходный код:
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
Я видел из исходного кода, что ThreadLocalMap присвоит каждому объекту ThreadLocal threadLocalHashCode при его сохранении.В процессе вставки, в соответствии с хэш-значением объекта ThreadLocal, он находит позицию i в таблице,int i = key.threadLocalHashCode & (len-1).
Затем он будет судить: если текущая позиция пуста, инициализировать объект Entry и поместить его в позицию i;
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
Если позиция i не пуста, если ключ этого объекта Entry оказался ключом, который нужно установить, то обновите значение в Entry;
if (k == key) {
e.value = value;
return;
}
Если позиция i не пуста и ключ не равен входу, найти следующую пустую позицию, пока она не станет пустой.
В этом случае при получении он также находит позицию в таблице в соответствии с хэш-значением объекта ThreadLocal, а затем оценивает, соответствует ли ключ в объекте Entry в позиции ключу получения, если нет. , оценить следующую позицию, установить Если конфликт с get серьезный, эффективность все равно очень низкая.
Ниже приведен исходный код get, хорошо ли его понимать:
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);
}
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
// get的时候一样是根据ThreadLocal获取到table的i值,然后查找数据拿到后会对比key是否相等 if (e != null && e.get() == key)。
while (e != null) {
ThreadLocal<?> k = e.get();
// 相等就直接返回,不相等就继续查找,找到相等位置。
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
Можете ли вы сказать мне, где хранятся объекты?
В Java стековая память принадлежит одному потоку, каждый поток будет иметь стековую память, а сохраненные переменные можно увидеть только в том потоке, которому они принадлежат, то есть стековую память можно понимать как частную память поток и объекты в куче памяти Видимые для всех потоков, объекты в куче памяти могут быть доступны для всех потоков.
Значит ли это, что экземпляр ThreadLocal и его значение хранятся в стеке?
На самом деле нет, потому что экземпляр ThreadLocal фактически удерживается созданным им классом (верхний должен удерживаться потоком), а значение ThreadLocal фактически удерживается экземпляром потока, все они находятся в куче, только через некоторые Трюк изменяет видимость на видимую для потока.
Что делать, если я хочу поделиться данными ThreadLocal потока?
использоватьInheritableThreadLocal
Несколько потоков могут получить доступ к значению ThreadLocal, мы создаем один в основном потоке.InheritableThreadLocal
экземпляр, а затем получить его в дочернем потокеInheritableThreadLocal
Значение параметра экземпляра.
private void test() {
final ThreadLocal threadLocal = new InheritableThreadLocal();
threadLocal.set("帅得一匹");
Thread t = new Thread() {
@Override
public void run() {
super.run();
Log.i( "张三帅么 =" + threadLocal.get());
}
};
t.start();
}
В дочернем потоке я могу нормально вывести эту строку журналов, что также является проблемой передачи данных между родительским и дочерним потоками, о которой я упоминал в видео-интервью ранее.
Как вы это передали?
Логика переноса очень проста, когда я упомянул threadLocals в коде Thread в начале, посмотрите вниз и увидите, что я намеренно поставил еще одну переменную:
В исходном коде Thread посмотрим, что делает Thread.init, когда он инициализируется и создается:
public class Thread implements Runnable {
……
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals=ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
……
}
Я перехватил часть кода, если потокinheritThreadLocals
Переменная не пуста, как в нашем примере выше, и родительский потокinheritThreadLocals
также существует, то я помещаю родительский потокinheritThreadLocals
для текущего потокаinheritThreadLocals
.
Это интересно?
Ребята, вы действительно много знаете, так что вы являетесь глубоким пользователем ThreadLocal.Вы нашли проблему с ThreadLocal?
Вы про утечку памяти?
Я проиграл, откуда этот пацан знает, о чем я хочу спросить? Хм, да, скажи мне.
Эта проблема действительно существует. Позвольте мне объяснить, почему. Помните приведенный выше код?
ThreadLocal сохранит себя как ключ в ThreadLocalMap при сохранении.Обычно и ключ, и значение должны строго ссылаться из внешнего мира, но теперь ключ разработан как слабая ссылка WeakReference.
Позвольте мне сначала представить вам слабые ссылки:
Объекты только со слабыми ссылками имеют более короткий жизненный цикл.Когда поток сборщика мусора сканирует область памяти, находящуюся под его юрисдикцией, как только будет найден объект только со слабыми ссылками, он будет переработан независимо от того, достаточно ли текущего места в памяти или нет. памяти.
Однако, поскольку сборщик мусора является потоком с очень низким приоритетом, объекты, имеющие только слабые ссылки, не обязательно будут обнаружены быстро.
Это приводит к проблеме. ThreadLocal будет переработан при сборке мусора, когда нет внешней строгой ссылки. Если поток, создавший ThreadLocal, продолжает работать, значение в объекте Entry может не перерабатываться постоянно, что приводит к утечкам памяти. .
Например, потоки в пуле потоков мультиплексированы.После обработки предыдущего экземпляра потока поток все еще выживает для целей мультиплексирования.Поэтому сохраняется значение, заданное ThreadLocal, что приводит к утечкам памяти.
Правда поток израсходован, ThreadLocalMap должен быть пустым, но теперь поток мультиплексирован.
Как это решить?
Просто используйте remove в конце кода, нам просто нужно не забыть использовать remove, чтобы очистить значение в конце кода.
ThreadLocal<String> localName = new ThreadLocal();
try {
localName.set("张三");
……
} finally {
localName.remove();
}
Исходный код удаления очень прост, все найденные соответствующие значения пусты, так что при переработке сборщиком мусора они будут переработаны автоматически.
Так почему же ключ ThreadLocalMap разработан как слабая ссылка?
Если для ключа не задана слабая ссылка, это вызовет тот же сценарий утечки памяти, что и значение в записи.
Еще один момент: я думаю, что отсутствие ThreadLocal можно компенсировать, посмотрев на netty fastThreadLocal Если вам интересно, вы можете хорошо провести время.
Ну, вы не только ответили на все, что я спросил, но и сказали даже то, чего я не знал, вы прошли тест ThreadLocal, но собеседование в JUC только началось, надеюсь, в будущем вы будете становиться все более и более смелым, и, наконец, получить хорошее предложение.
Какого черта, неужели мне не стыдно вдруг быть такой эмоциональной? Мастеру тяжело думать обо мне так, и я продолжала ругать его в своем сердце, не говоря уже о том, чтобы вернуться и усердно учиться.
Суммировать
На самом деле использование ThreadLocal очень простое, в нем всего несколько методов, в закомментированном исходном коде не так много строк, я потратил больше десяти минут, чтобы пройти его, но когда я углубляюсь в логику за каждым методом я также не могу не чувствовать, насколько круты Джош Блох и Дуг Ли.
На самом деле, обращение с детальным проектированием часто является разницей между нами и Великим Богом.Я думаю, что есть много неразумных моментов.После того, как гугл и я продолжаю разбираться в них, я нахожу, что это разумно, и я действительно отказываюсь это принять .
ThreadLocal — относительно непопулярный класс в многопоточности, он используется не так часто, как другие методы и классы, но благодаря моей статье мне интересно, есть ли у вас какие-то новые знания?
Я Ао Бин, чем больше ты знаешь, тем больше ты не знаешь, увидимся в следующий раз!
талантнаш【Три подряд】Это самая большая движущая сила для создания Ao Bing.Если в этом блоге есть какие-либо ошибки и предложения, вы можете оставить сообщение!
Статья постоянно обновляется, вы можете искать в WeChat "Ао Бинг«Прочитайте это в первый раз и ответьте, обратив внимание [материал] Подготовленные мной материалы интервью и шаблоны резюме крупных заводов первой линии, эта статьяGitHub github.com/JavaFamilyОн был включен, и есть полные тестовые сайты для интервью с крупными заводами.Добро пожаловать в Star.