содержание
- содержание
-
предисловие
- 1. Найдите расстояние до самого дальнего узла бинарного дерева
- 2. Упаковка и распаковка Java
- 3. Когда пользовательский поток будет приостановлен в процессе сбора сборщиком мусора CMS?
- 4. Почему ConcurrentHashMap не нужно блокировать в процессе чтения?
- 5. В чем разница между перефразировкой словаря Redis и перефразировкой, такой как hashmap в JDK?
- Справочная статья
предисловие
Недавно я участвовал в интервью с бэкенд-инженером в Toutiao, и оно было жалким, и оно просто умерло.
Вернувшись, я также подвел некоторые итоги процесса интервью, и я не буду приносить личные вещи.Эта статья в основном посвящена обзору технических проблем в процессе интервью.
1. Найдите расстояние до самого дальнего узла бинарного дерева
Это оригинальный вопрос LeetCode,Ссылка на оригинальное название
Найдите узлы между самыми дальними узлами бинарного дерева. Сначала резюмируем несколько правил:
- Диаметр дерева либо полностью находится в его левом поддереве, либо полностью находится в его правом поддереве, либо проходит через корневой узел.
- Диаметр дерева = Возьмите каждый узел дерева в качестве корневого узла и найдите максимальный диаметр прохождения через корневой узел, а максимальное значение всех узлов - это диаметр дерева.
- Чтобы найти диаметр, проходящий через корневой узел, его можно разложить на: найти самый глубокий лист левого поддерева + самый глубокий лист правого поддерева.
Итак, мы делаем:
- Рекурсивно найти каждый узелПередача диаметра корневого узла (найти самый глубокий лист левого поддерева текущего узла и самый глубокий лист правого поддерева), принять его максимальное значение.
- Поиск самого глубокого листа узла равен большему из самого глубокого листа его левого поддерева + 1 и самого глубокого листа правого поддерева + 1.
- При нахождении самого глубокого листа мы фактически находим диаметр текущего узла (самый глубокий лист слева + самый глубокий лист справа + 2), во избежание повторных вычислений, при рекурсивном поиске самого глубокого листа диаметр тоже записывается)
код показывает, как показано ниже:
public int diameterOfBinaryTree(TreeNode root) {
AtomicReference<Integer> ret = new AtomicReference<>(0);
find(root, ret);
return ret.get();
}
private int find(TreeNode node, AtomicReference<Integer> result) {
if (node == null) return 0;
int left = 0, right = 0;
if (node.left != null) left = find(node.left,result) + 1;
if (node.right != null) right = find(node.right,result) + 1;
int tmp = Math.max(result.get(), left + right);
result.set(tmp);
return Math.max(left, right);
}
Код очень прост, он рекурсивно находит самый дальний лист левого поддерева и самый дальний лист правого поддерева, Затем в процессе вычисления当前节点的直径Сохраните его как альтернативу и, наконец, найдите максимальный диаметр.
2. Упаковка и распаковка Java
Java добавила механизм автоматической упаковки и распаковки в 1.5.В общем, это в основном автоматическое преобразование между базовым типом и соответствующим типом упаковки.
Как в коде ниже:
public class BoxTest {
public static void main(String [] args){
Integer a = 10; // 装箱
int b = a; // 拆箱
}
}
Декомпилируем код после компиляции, можете посмотреть
Очевидно в коде#2 ,#3В упаковке и без коробки.
Целые называются соответственноvalueOfМетоды иintValueметод.
3. Когда пользовательский поток будет приостановлен в процессе сбора сборщиком мусора CMS?
Здесь не объясняются все сборщики мусора, заинтересованные друзья могут двигатьсяОбласть данных JVM и сборка мусора.
Как мы все знаем, процесс сборки мусора в CMS выглядит следующим образом:
Следовательно, по-прежнему необходимо приостанавливать пользовательский поток на двух этапах начальной маркировки и перемаркировки.
4. Почему ConcurrentHashMap не нужно блокировать в процессе чтения?
ПроверятьConcurrentHashMapИсходный код можно найти, определение узла Node:
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
}
Как видите, в нем определены несколько свойств, а именно:
- Окончательное измененное хеш-значение нельзя изменить снова после инициализации.
- Окончательный измененный ключ нельзя изменить снова после инициализации.
- изменчивое декорированное значение
- изменчивый декорированный указатель следующего узла
существуетget(Ojbect)во время вызова метода.
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
//获取hash值
int h = spread(key.hashCode());
//通过tabat获取hash桶, tabAt是一个线程安全的操作, 有UnSafe来保证的.
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) {
//如果该hash桶的第一个节点就是查找结果,则返回
if ((eh = e.hash) == h) {
if ((ek = e.key) == key || (ek != null && key.equals(ek)))
return e.val;
}
//第一个节点是树的根节点,按照树的方式进行遍历查找
else if (eh < 0)
return (p = e.find(h, key)) != null ? p.val : null;
//第一个节点是链表的根节点,按照链表的方式遍历查找
while ((e = e.next) != null) {
if (e.hash == h &&
((ek = e.key) == key || (ek != null && key.equals(ek))))
return e.val;
}
}
return null;
}
В этом процессе
- Получите корневой узел хеш-ковша через
tabAtдля работы, потокобезопасный. - При обходе используется атрибут next узла, поскольку он модифицируется с помощью volatile, он виден между потоками, и возникают проблемы параллелизма.
- Прочитайте изменчивое свойство val узла при возврате.
такgetОбъект можно получить корректно без блокировки в процессе.
5. В чем разница между перефразировкой словаря Redis и перефразировкой, такой как hashmap в JDK?
Этот вопрос относительно широк, и мое личное понимание состоит из следующих двух моментов.
- При повторном хэшировании хэш-карты временно создается другая таблица. Redis постоянно сохраняет ссылку на две таблицы. Достаточное пространство выделяется только тогда, когда требуется повторное хеширование.
- Повторное хеширование Hashmap — это одноразовое централизованное завершение процесса повторного хеширования, а Redis — это прогрессивное хеширование.
Процесс повторного хеширования hashmap должен быть понятен всем, поэтому давайте поговорим о прогрессивном хешировании redis.
Прежде всего, rehash — это повторное хеширование всех данных в исходной таблице и сохранение их в новой таблице для расширения.
Redis — это однопоточный высокопроизводительный сервис, если в хеш-таблице сотни миллионов данных, перехеширование займет много времени, в этот период Redis не может предоставлять внешние сервисы, что недопустимо.
Поэтому Redis реализует прогрессивное хеширование, процесс выглядит следующим образом:
- Если текущие данные находятся в ht[0], то сначала выделите достаточно места для ht[1].
- В словаре поддерживается переменная, rehashindex = 0. Она используется для указания хода выполнения текущего перефразирования.
- Во время повторного хеширования каждый раз, когда словарь добавляется, удаляется, изменяется и выполняется поиск, после завершения фактической операции будет выполняться операция повторного хеширования, и ht[0] будет помещен в
rehashindexПерефразируйте значение в позиции в ht[1].Увеличьте rehashindex на единицу. - При непрерывном выполнении все значения исходного ht[0] всегда будут перехэшироваться, и на этом процесс перехеширования завершится.
В описанном выше процессе не упомянуты две проблемы:
- Что, если сервер очень свободен?Если в течение нескольких часов не будет поступать запросов, не будет ли пустой тратой памяти хранить две таблицы одновременно?
Решение таково: в функцию времени redis также добавлена операция помощи перехешированию, так что если сервер простаивает, перехеширование будет выполнено быстрее.
- Каким образом хэш-таблица предоставляет услуги внешнему миру в период обслуживания двух таблиц?
Решение: для операции добавления добавьте ее непосредственно в ht[1], чтобы это могло гарантировать, что количество ht[0] будет только уменьшаться, а не увеличиваться, чтобы гарантировать, что процесс перефразирования может быть завершен. изменять, запрашивать и другие операции будут в Выполнить это на ht[0], Если результат не получен, он перейдет к ht[1] и выполнит его снова.
Преимущества прогрессивного хеширования очевидны: оно использует идею «разделяй и властвуй» и распределяет операцию повторного хеширования на каждую операцию хеш-таблицы, избегая нагрузки на производительность, создаваемой централизованным повторным хешированием.
В то же время прогрессивное хеширование также приносит проблему, то есть в момент перехеширования нужно сохранить две хеш-таблицы, что занимает немного больше памяти, и если сервер redis переполнен памятью, он внезапно выполняется ● Повторное хеширование приведет к отбрасыванию большого количества ключей.
Справочная статья
Проектирование и реализация Redis (второе издание)
Заканчивать.
ChangeLog
2019-05-19 ЗавершеноВсе вышеизложенное является личными мыслями, если есть какие-либо ошибки, пожалуйста, исправьте их в комментариях.
Добро пожаловать на перепечатку, пожалуйста, подпишите и сохраните исходную ссылку.
Контактный адрес электронной почты: huyanshi2580@gmail.com
Дополнительные заметки об обучении см. в личном блоге или подпишитесь на общедоступную учетную запись WeChat ------>Хуян тен