Задачник Java
Задачник Java
Flutter
1C Рассказать про ThreadPool в Java. Что это такое, зачем они нужны, когда их нужно
применять, какие виды ThreadPool бывают.
Аналитика данных
C++ Ответ
GO Пулы потоков используют, когда нужно выполнять несколько задач одновременно. Они
помогают управлять количеством потоков и улучшить производительность приложения.
Architecture (System Design)
18 грейд
QA
Пулы потоков — это механизм из Executor framework, который позволяет
.NET переиспользовать фиксированное количество потоков для выполнения большого числа
задач. Это снижает затраты на создание и уничтожение потоков.
Android
iOS Пулы потоков полезны, когда нужно выполнять много задач параллельно, но при этом
важно контролировать количество потоков, чтобы избежать истощения ресурсов.
SQL Например, на сервере для обработки множества клиентских запросов. Это снижает
задержки и улучшает производительность системы.
BI
19 грейд
Java
Пулы потоков в Java - это часть Executor framework, которая управляет пулом
Задачи переиспользуемых потоков для выполнения задач. Они важны в высококонкурентных
приложениях, где создание и уничтожение потоков может быть дорогостоящим. Пул
Знание языка и ООП потоков позволяет эффективно управлять выполнением задач, ограничивая количество
одновременно работающих потоков.
Коллекции и стримы
Дженерики Пулы потоков используют, когда нужно выполнять много задач одновременно, но вы
хотите избежать затрат на создание/уничтожение потоков и возможного истощения
Многопоточность ресурсов. Например, на веб-сервере каждую входящую задачу можно обрабатывать
потоком из пула.
JVM / GC
DS
Мотивация
2. Механизмы синхронизаций и блокировок
Базы данных(не для Data Java Многопоточность
Engineer)
17 - 20 грейд 5 мин 191 раз Свернуть
Аналитик (бизнес)
https://matrix.o3.ru/trials 1/23
07.10.2024, 15:50 Задачник
Проектное управление Механизмы синхронизации используются для управления доступом нескольких потоков к
общим ресурсам, чтобы избежать одновременного изменения данных и предотвратить
Системная аналитика ошибки.
18 грейд
Механизмы синхронизации обеспечивают безопасный доступ нескольких потоков к общим
ресурсам. В Java основные средства синхронизации — это ключевое слово synchronized ,
блокировки (Locks) и атомарные переменные (Atomic Variables).
19 грейд
Механизмы синхронизации в Java включают synchronized блоки, методы, а также более
продвинутые инструменты, такие как Lock из java.util.concurrent и атомарные
операции. Они обеспечивают управление доступом потоков к разделяемым ресурсам,
предотвращая race conditions и deadlocks.
Библиотека java.util.concurrent :
• Locks: ReentrantLock , ReadWriteLock , StampedLock – их преимущества над synchronized ,
особенности использования и когда лучше применять каждый из них.
• Condition: Использование Condition с Lock для управления более сложными
взаимодействиями между потоками (например, для реализации очередей с
ожиданием).
• Semaphores: Использование семафоров для управления доступом к ограниченным
ресурсам.
• CountDownLatch: Для ожидания завершения операций несколькими потоками.
• CyclicBarrier: Для синхронизации точек, где несколько потоков должны одновременно
завершить выполнение.
Volatile:
• Понимание использования volatile для обеспечения видимости изменений
переменных между потоками и предотвращения проблем с кэшированием и
переупорядочением операций.
Non-blocking algorithms:
• Знание неблокирующих алгоритмов и структур данных, таких как ConcurrentHashMap ,
ConcurrentLinkedQueue , и их внутреннее устройство.
ThreadLocal:
• Использование ThreadLocal для создания переменных, специфичных для каждого
потока, что позволяет избежать необходимости синхронизации в некоторых случаях.
Барьеры и соглашения:
• Понимание happens-before отношений в Java Memory Model и как они влияют на
синхронизацию и видимость данных между потоками.
Многопоточная оптимизация:
• Знание продвинутых техник оптимизации многопоточных приложений, таких как
минимизация contention, снижение нагрузки на синхронизацию, и использование lock-
free структур данных.
20 грейд
Вариант ответа для кандидатов, которые перечислили ощутимую часть элементов из
предыдущего пункта и показали гоубокое понимание деталей
Ответ
https://matrix.o3.ru/trials 2/23
07.10.2024, 15:50 Задачник
17 грейд
int — это примитивный тип данных, который хранит целое число. Integer — это класс-
обертка для int , который позволяет работать с целыми числами как с объектами.
18 грейд
int — это примитивный тип, который хранит значение целого числа напрямую в памяти.
Integer — это объектный тип (класс-обертка), который предоставляет дополнительные
методы для работы с целыми числами и может использоваться в коллекциях, требующих
объектов. Также Integer поддерживает null , тогда как int не может быть null .
Объекты: хранятся в куче, могут иметь значение null, передаются по ссылке, надо
реализовывать метод equals для сравнения, есть заголовок.
19 грейд
int — это примитивный тип, который хранит значение целого числа в памяти,
обеспечивая высокую производительность и минимальное потребление памяти. Integer
— это класс-обертка, который позволяет использовать примитивные целые числа в
контексте, где требуются объекты, например, в коллекциях, таких как List или Map .
Integer поддерживает автобоксинг и анбоксинг, что позволяет автоматически
преобразовывать int в Integer и наоборот. Однако это может приводить к накладным
расходам на память и производительность. Также, Integer поддерживает значение null ,
что может быть полезно для обозначения отсутствия значения, но требует
дополнительной обработки, чтобы избежать NullPointerException .
Кэширование значений в диапазоне от -128 до 127 (Integer cache) и как это влияет на
производительность и сравнение объектов Integer .
Ответ
17 грейд
Для частого удаления первого элемента лучше использовать LinkedList , потому что в нем
удаление первого элемента выполняется быстро.
18 грейд
LinkedList — оптимальный выбор для частого удаления первого элемента, так как в
LinkedList операция удаления первого элемента (метод removeFirst() ) выполняется за
O(1). Это связано с тем, что LinkedList реализован как двусвязный список, где первый
элемент легко удаляется без сдвига остальных элементов.
19 грейд
LinkedList — оптимальная структура данных для частого удаления первого элемента,
поскольку операция удаления первого элемента выполняется за O(1) благодаря
двусвязной природе списка.
5. Enum из прошлого
Java Задачи
Реализовать свой перечислимый тип (enum), как если бы до появления современного enum
в Java 1.5.
Нужно реализовать контракт современного Java-enum:
https://matrix.o3.ru/trials 3/23
07.10.2024, 15:50 Задачник
• можно легко получать любое значение энума
• безопасное сравнение значений по ссылке (`==`)
• каждое значение имеет строковое имя, совпадающее с названием значения
• каждое значение имеет целочисленный идентификатор `ordinal`, который содержит
номер значения в порядке его объявления в энуме
• можно получить список всех значений энума
• можно получить значение по его `ordinal`
• можно получить значение по его имени
Для примера можно взять список валют. Важно, чтобы список был потенциально
расширяемым, потому что качество кода будет определять, насколько беспроблемно в
будущем пройдет добавление значений в энум.
class Currency {
// TODO
}
Ответ
18 грейд
Решение должно выглядеть примерно так:
19 грейд
https://matrix.o3.ru/trials 4/23
07.10.2024, 15:50 Задачник
Еще лучше, если кандидат сможет отказаться от передачи имени в конструкторе:
static {
for (Field field : Currency.class.getFields()) {
if (Modifier.isStatic(field.getModifiers()) &&
field.getType().equals(Currency.class)) {
try {
Currency currency = (Currency) field.get(null);
currency.setName(field.getName());
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
private Currency() {
ordinal = values.size();
values.add(this);
}
Дополнительные вопросы
https://matrix.o3.ru/trials 5/23
07.10.2024, 15:50 Задачник
• поле `ordinal` вычисляется автоматически, без явного указания в конструкторе
• метод, возвращающий список всех значений энума защищает этот список от внешних
изменений (а если это массив, то создавать копию массива)
• методы, возвращающие значение по `ordinal`/имени, используют для поиска `Map` или
`List` или поиск по индексу в массиве, а также обрабатывают ситуацию, когда значение
не найдено (кидают исключение или возвращают `Optional`)
• порядок следования статических полей правильный: поля, используемые в
конструкторе, объявлены до самих значений (иначе в конструкторе будет NPE)
Усложнение задачи:
• Реализовать базовый абстрактный класс для любого перечислимого типа
6. Класс и интерфейс
Java Знание языка и ООП
Ответ
17 грейд
Class — это шаблон для создания объектов, который может содержать поля и методы.
Interface — это набор абстрактных методов, которые класс должен реализовать.
Модификаторы доступа
• Для class можно использовать модификаторы доступа public , default , private ,
protected .
• Для методов и полей в interface — только public и default .
18 грейд
Class — это структура, содержащая поля и методы, которая может наследоваться от
других классов и реализовывать интерфейсы. Interface определяет набор методов,
которые класс должен реализовать.
Что такое Jar hell / classpath hell / dependency hell? Какие последствия, как отловить, как
исправить?
Ответ
Jar hell / classpath hell / dependency hell - ситуация, когда в classpath попадают не те
версии классов, которые ожидались (или вообще отсутствуют). Например, в конфигурации
сборки есть конфликтующие зависимости (или несколько версий одной библиотеки). В
такой ситуации неизвестно из какого jar ClassLoader загрузит классы.
Если при этом есть несовместимые отличия в ABI (например изменения интерфейсов,
сигнатур, появление или исчезновение публичных полей классов) и код библиотеки не
вызывается напрямую из приложения, то мы получим соответствующую ошибку в
рантайме (NoClassDefFoundError, NoSuchMethodError).
https://matrix.o3.ru/trials 6/23
07.10.2024, 15:50 Задачник
8. Гипотезы о поколениях
Java JVM / GC
Ответ
18 грейд
Гипотеза о поколениях предполагает, что большинство объектов в куче Java "умирают"
молодыми, то есть они быстро становятся недоступными и удаляются сборщиком мусора,
не переходя в область старого поколения. Это знание используется для оптимизации
работы сборщика мусора, разделяя кучу на "молодое" и "старое" поколения, что улучшает
производительность за счет быстрой очистки молодых объектов.
19 грейд
Первая гипотеза (aka "слабая"): большинство объектов умирает молодыми.
Дополнительные вопросы
9. Имплементации GC
Java JVM / GC
Ответ
17 грейд
--
18 грейд
Serial GC: Использует один поток для выполнения сборки мусора. Простой и эффективный
для однопоточных приложений или систем с малым количеством процессоров.
Parallel GC (Parallel New GC): Использует несколько потоков для сборки мусора в молодом
поколении, что улучшает производительность на многопроцессорных системах.
19 грейд
CMS (Concurrent Mark-Sweep) GC: Параллельный сборщик, минимизирующий паузы,
работает одновременно с приложением. Однако, он может быть менее эффективен с точки
зрения использования CPU и памяти.
https://matrix.o3.ru/trials 7/23
07.10.2024, 15:50 Задачник
20 грейд
Serial GC, Parallel GC, G1 GC, CMS GC: Эксперт понимает их внутренние механизмы, плюсы
и минусы, и знает, как настраивать эти GC для конкретных сценариев.
Epsilon GC: "No-op" сборщик мусора, который не выполняет сборку мусора. Используется
для тестирования и анализа поведения приложения без вмешательства сборщика мусора.
Ответ
17 грейд
GC (Garbage Collector) — это сборщик мусора в Java, который автоматически освобождает
память, занимаемую объектами, которые больше не используются. Это помогает избежать
утечек памяти и упрощает управление памятью, поскольку разработчику не нужно
вручную освобождать память.
18 грейд
GC (Garbage Collector) — это механизм в Java, который управляет памятью, автоматически
обнаруживая и удаляя объекты, которые больше не используются в программе. Он
освобождает разработчика от необходимости вручного управления памятью, снижает
риск утечек памяти и обеспечивает эффективное использование памяти. Сборщик мусора
работает в фоновом режиме, но его работа может влиять на производительность
приложения, создавая паузы в выполнении программы.
19 грейд
GC (Garbage Collector) в Java — это автоматизированный механизм управления памятью,
который отвечает за освобождение памяти, занятой объектами, которые больше не
доступны для использования. Его основная задача — предотвратить утечки памяти и
обеспечить эффективное использование кучи (heap). GC освобождает разработчиков от
ручного управления памятью, что снижает вероятность ошибок, таких как "dangling
pointers" или "double free". Однако работа GC может приводить к паузам в работе
приложения, что критично для систем реального времени. Senior разработчик должен
понимать, как разные алгоритмы сборки мусора (например, G1, CMS, ZGC) влияют на
производительность, и уметь настраивать параметры GC для достижения баланса между
временем пауз и пропускной способностью (throughput) приложения.
Дополнительные вопросы
https://matrix.o3.ru/trials 8/23
07.10.2024, 15:50 Задачник
Ответ
17 грейд
Код можно запустить в отдельном потоке, создав класс, который реализует интерфейс
Runnable , и передав его объект в Thread , а затем вызвать метод start() .
18 грейд
Использовать ExecutorService из java.util.concurrent для управления потоками,
например, executor.submit() для запуска задачи в отдельном потоке:
executor.submit(() -> {
// Код, который будет выполнен в отдельном потоке
});
executor.shutdown();
19 грейд
CompletableFuture: Подходит для асинхронных операций, особенно при работе с
цепочками зависимых задач.
CompletableFuture.runAsync(() -> {
// Код, который будет выполнен асинхронно
});
12. Volatile
Java Многопоточность
Ответ
17 грейд
volatile — это ключевое слово в Java, которое используется для переменных, чтобы
указать, что их значение может изменяться другими потоками. Переменные с volatile
всегда читаются и записываются непосредственно из основной памяти, избегая
кэширования в потоке.
18 грейд
volatile — это модификатор переменной в Java, который гарантирует, что значение
переменной всегда будет читаться и записываться напрямую из основной памяти. Это
предотвращает кэширование значения переменной в локальных кэшах потоков, что может
привести к несогласованности данных между потоками. Использование volatile
обеспечивает видимость изменений переменной для всех потоков, но не гарантирует
атомарность операций над переменной.
https://matrix.o3.ru/trials 9/23
07.10.2024, 15:50 Задачник
19 грейд
Кандидат должен быть знаком с JMM, понимать принцип Happens-Before, знать основы
меж-поточного взаимодействия.
• Volatile обеспечивает атомарную запись для long и double переменных вне зависимости
от платформы.
• A write to a volatile field happens-before every subsequent read of that field.
13. Synchronized
Java Многопоточность
Ответ
17 грейд
synchronized — это ключевое слово в Java, которое используется для синхронизации
потоков. Оно обеспечивает, что только один поток может выполнять блок кода или метод,
помеченный synchronized , в данный момент времени, тем самым предотвращая
одновременный доступ к общим ресурсам.
18 грейд
synchronized — это механизм синхронизации в Java, который обеспечивает эксклюзивный
доступ к методу или блоку кода для одного потока за раз. Когда поток входит в
synchronized метод или блок, он захватывает монитор объекта, что предотвращает другие
потоки от одновременного выполнения этого же кода. synchronized помогает
предотвратить состояние гонки (race condition), но может привести к блокировкам
(deadlocks), если не использовать его осторожно.
19 грейд
synchronized — это ключевое слово и механизм в Java, обеспечивающий блокировку
доступа к критическому участку кода, чтобы только один поток мог его выполнить в
данный момент времени. Использование synchronized приводит к захвату монитора
объекта, что предотвращает другие потоки от одновременного доступа к этому блоку кода
или методу. Это гарантирует видимость изменений, сделанных внутри synchronized блока,
для всех потоков, а также предотвращает переупорядочивание инструкций в пределах
блока.
14. Atomic-ки
Java Многопоточность
https://matrix.o3.ru/trials 10/23
07.10.2024, 15:50 Задачник
Что такое Atomic-ки в Java? Приведите примеры использования? Как устроены внутри?
Ответ
17 грейд
Атомарные классы в Java, такие как AtomicInteger , обеспечивают безопасные для потоков
операции без использования synchronized . Они позволяют выполнять операции, такие как
инкремент или сравнение и установка, атомарно, то есть без прерывания другим потоком.
18 грейд
Атомарные классы в Java, такие как AtomicInteger , AtomicLong , и AtomicReference ,
обеспечивают атомарные операции, такие как инкремент, декремент и сравнение-
установка (compare-and-set, CAS), без использования блокировок. Они необходимы для
создания потокобезопасных алгоритмов с высокой производительностью, особенно в тех
случаях, когда использование synchronized может быть излишне дорогим и снижает
производительность.
19 грейд
Atomic* - это набор классов, имеющих полезные комплексные методы с гарантией
атомарности выполнения.
Ответ
Во втором (secondExecutor)
Дополнительные вопросы
https://matrix.o3.ru/trials 11/23
07.10.2024, 15:50 Задачник
18 - 19 грейд 3 мин 1 раз Свернуть
Ответ
18 грейд
Никак, они затираются при компиляции.
19 грейд
Есть трюк с ParameterizedType:
Ответ
17 грейд
HashMap хранит данные в произвольном порядке и обеспечивает быстрый доступ по
ключу. TreeMap хранит данные в отсортированном порядке по ключу. Используйте
HashMap , когда не важен порядок, и TreeMap , когда нужен отсортированный порядок
ключей
18 грейд
HashMap реализует хэш-таблицу, обеспечивая быстрый доступ к элементам с временем
операции O(1). Порядок элементов не гарантирован. TreeMap реализует красно-черное
дерево и хранит элементы в отсортированном порядке по ключу, обеспечивая время
доступа O(log n). Используйте HashMap для быстрого доступа к данным без требования
порядка. TreeMap подходит, когда важен отсортированный порядок ключей или требуется
выполнять операции диапазона ( subMap , headMap , tailMap ).
19 грейд
HashMap — это структура данных, основанная на хэш-таблице, обеспечивающая
амортизированное время вставки, удаления и поиска O(1), но без гарантий порядка
хранения элементов. TreeMap , с другой стороны, основан на красно-черном дереве и
поддерживает порядок ключей в соответствии с их естественным порядком или с
помощью компаратора. Операции вставки, удаления и поиска в TreeMap выполняются за
O(log n).
18. ConcurrentHashMap
Java Коллекции и стримы
Ответ
18 грейд
ConcurrentHashMap — это потокобезопасная версия HashMap , которая позволяет
нескольким потокам одновременно выполнять операции чтения и записи без
необходимости полной блокировки всей структуры данных.
19 грейд
Внутреннее устройство ConcurrentHashMap отличается от HashMap и Hashtable тем, что оно
оптимизировано для многопоточного доступа.
19. HashMap
Java Коллекции и стримы
Ответ
17 грейд
HashMap в Java — это структура данных, которая хранит пары "ключ-значение". Внутри она
использует массив, в котором хранятся элементы, распределенные по корзинам (buckets).
https://matrix.o3.ru/trials 13/23
07.10.2024, 15:50 Задачник
Корзина выбирается на основе хэш-кода ключа.
18 грейд
HashMap в Java реализован как массив, в котором элементы (пары "ключ-значение")
хранятся в виде связных списков или деревьев (с Java 8).
Когда добавляется новый элемент, хэш-код ключа используется для определения индекса
корзины (bucket) в массиве. Если несколько ключей попадают в одну и ту же корзину
(коллизия), они добавляются в связанный список.
19 грейд
Внутреннее устройство HashMap включает в себя массив Node<K,V>[] , где каждый элемент
массива представляет собой корзину (bucket). При добавлении пары "ключ-значение" хэш-
код ключа используется для вычисления индекса корзины.
HashMap также использует методы, такие как resize() для динамического увеличения
размера массива, когда количество элементов превышает определенный коэффициент
загрузки (load factor). При этом элементы перераспределяются в новом массиве, что также
может привести к перехэшированию.
20. Hashcode
Java Коллекции и стримы
Ответ
17 грейд
Класс должен переопределить методы hashCode() и equals() так, чтобы можно было
корректно сравнивать ключи и вычислять их хэш-коды.
18 грейд
Чтобы использовать объекты класса в качестве ключей в HashMap , класс должен
корректно переопределять методы hashCode() и equals() .
Метод equals() должен определять логическое равенство двух объектов, чтобы HashMap
мог корректно находить и сравнивать ключи.
Важно, чтобы два объекта, которые считаются равными (по методу equals() ), имели
одинаковые хэш-коды (по методу hashCode() ), иначе HashMap не сможет корректно
работать с этими ключами.
19 грейд
Для использования объектов класса в качестве ключей в HashMap , класс должен
корректно переопределять методы hashCode() и equals() .
Основные требования:
https://matrix.o3.ru/trials 14/23
07.10.2024, 15:50 Задачник
1. Переопределение hashCode() : Этот метод должен возвращать одинаковый хэш-код для
объектов, которые считаются равными по методу equals() . Это необходимо для того,
чтобы объекты распределялись в правильные корзины (buckets) в HashMap .
2. Переопределение equals() : Этот метод должен обеспечивать правильное сравнение
объектов на равенство. Если два объекта равны (по equals() ), они должны иметь
одинаковый хэш-код. Если equals() не переопределен, то сравнение будет
выполняться по умолчанию, используя ссылочное равенство (то есть == ), что может
быть неприемлемо для объектов-сравниваемых ключей.
3. Согласованность hashCode() и equals() : Важно, чтобы эти методы работали
согласованно: если equals() определяет два объекта как равные, то их hashCode()
должен быть одинаковым. В противном случае, объекты могут оказаться в разных
корзинах, и HashMap не сможет их корректно найти или обработать.
Будет плюсом если кандидат скажет про высокую кардинальность hashCode и мощность
множества значений.
Дополнительные вопросы
Q: Если кандидат ответил, что "объект должен быть иммутабельным", то стоит спросить
является ли это требование достаточным.
Ответ
17 грейд
ArrayList — это структура данных на основе массива, обеспечивающая быстрый доступ к
элементам по индексу.
LinkedList — это структура данных на основе связного списка, где элементы связаны
друг с другом через ссылки.
18 грейд
ArrayList реализован на основе динамического массива, что обеспечивает O(1) доступ к
элементам по индексу, но вставка и удаление элементов в середине списка требуют O(n)
из-за необходимости сдвига элементов.
LinkedList основан на двусвязном списке, где каждый элемент (узел) содержит ссылки на
предыдущий и следующий узлы, что позволяет выполнять вставку и удаление за O(1), но
доступ к элементу по индексу требует O(n) времени, так как необходимо пройти список от
начала или конца.
19 грейд
ArrayList - список на массиве, который расширяется в два раза при добавлении элементов.
LinkedList - двусвязный список.
- ArrayList: сложность вставки в начало и середину - O(N), в конец - O(1) (если говорить
про амортизированную сложность).
- LinkedList: сложность вставки в начало и конец - O(1), в середину - O(N).
- Доступ по индексу: ArrayList - O(1), LinkedList - O(N)
https://matrix.o3.ru/trials 15/23
07.10.2024, 15:50 Задачник
// 1
Integer a = Integer.valueOf(100);
Integer b = Integer.valueOf(100);
System.out.println(a == b);
// 2
Integer a = Integer.valueOf(1000);
Integer b = Integer.valueOf(1000);
System.out.println(a == b);
// 3
String a = "str";
String b = "str";
System.out.println(a == b);
Ответ
23. Дедлок
Java Задачи
Ответ
import java.util.concurrent.CountDownLatch;
first.start();
second.start();
first.join();
second.join();
}
https://matrix.o3.ru/trials 16/23
07.10.2024, 15:50 Задачник
System.out.println(threadName + " thread taken " + o1);
latch.countDown();
latch.await();
synchronized (o2) {
System.out.println(threadName + " thread taken " +
o2);
}
}
} catch (InterruptedException ignored) {
}
}
}
24. Сервер-счётчик
Java Задачи
Есть теоретическая машина на 100 ядер, на нём запущен HTTP сервер на Java, который
работает на 100 потоках. Всё что делает этот сервер -- считает количество запросов на
него с момента старта сервера.
@Singleton
public class Handler {
/**
* Этот метод записывает информацию о каждом разе, когда он был
вызван и ничего не возвращает.
* Этот метод находится под большой нагрузкой, мы хотим иметь
максимальную пропускную способность для него.
*/
@POST("/register_event")
public void registerEvent(Req req) {
//
}
/**
* Этот метод возвращает сколько раз был вызван метод /register_event
с момента старта сервера.
* Этот метод вызывается редко, 1 раз в минуту.
*/
@GET("/events_number")
public int registerEvent(Req req) {
return //...
}
}
Ответ
17 грейд
Самый плохой вариант - synchronized вокруг переменной.
18 грейд
Ещё один плохой вариант: AtomicInteger / AtomicLong . Но при высоком contention (как в
этом примере) и на многосокетных тачках будет работать очень плохо. Возможно, даже
хуже чем synchronized .
19 грейд
Чуть-чуть получше -- решение в акторном стиле: пишем в очередь события, отдельный
поток слушает очередь и обновляет в переменную. Из неё также читаем по запросу к
актору. Но нужно сделать очень грамотную очередь, которая умеет эффективно принимать
из многих потоков данные (Multi-producer/single-consumer). Лучше synchronized, т.к. поток
HTTP сервера не блочится на запись.
https://matrix.o3.ru/trials 17/23
07.10.2024, 15:50 Задачник
Ещё лучше: LongAdder . Имеет лучшую пропускную по сравнению
с AtomicInteger способность за счёт наличия нескольких ячеек, между которым
распределяется запись. При высоком Contention имеет хорошую пропускную способность.
20 грейд
Более-менее масштабируемый вариант: у каждого потока есть своя собственная
локальная переменная, в которую он пишет. Для чтения считаем сумму по всем потокам.
Дополнительный плюс, если кандидат упомянет false-sharing проблему: если значения
будут лежать в плотном массиве или вообще рядом в памяти, то счётчики из разных
потоков будут попадать в одну кэш-линию, что также снизит производительность.
Дополнительные вопросы
Ответ
Ответ
Нужно реализовать "кэш", который хранит лонги по индексу. Размер кэша - 10 элементов.
Реализует два метода: void bulkUpdate(Updater updater) и long[] bulkRead(int[]
indices) .
• bulkUpdate обновляет значения пачкой в текущем состоянии кэша, in-place.
• bulkRead получает пачкой необходимые лонги из кэша по индексам.
Условия:
• Есть N (константа) потоков которые кэш читают.
https://matrix.o3.ru/trials 18/23
07.10.2024, 15:50 Задачник
• Есть 1 поток, который кэш обновляет.
• Читатель должен быть защищён от dirty-read. То есть, если происходит мутация А -> B,
то читатель должен видеть только конечное состояние (A или B), но никогда
промежуточное.
• Чтение должно быть неблокирующим.
• Запись может быть блокирующей.
• Входные данные можно считать всегда влидными (не null; индексы только от 0 до 9
включительно).
interface Cache {
//Метод для обновления кэша через мутацию
void bulkUpdate(Updater updater);
Ответ
https://matrix.o3.ru/trials 19/23
07.10.2024, 15:50 Задачник
Задача проверяет понимает ли кандидат основы JMM, как работает volatile.
Без volatile или AtomicReference (фактически тоже самое в этом случае) решение не
является корректным. Подробнее: https://gitlab.ozon.ru/ykornev/cache-jcstress-test/
Дополнительные вопросы
/**
* Создаёт очередь элементов типа int с заданным максимальным
размером
*/
public ArrayBlockingQueue(int maxSize) {
}
/**
* Добавляет элемент типа int в очередь, ждёт (блокируется) если
очередь заполнена
*/
public void offer(int elt) {
}
/**
* Возвращает и удаляет следующий элемент из очереди, ждёт
(блокируется) если очередь пустая
*/
public int take() {
}
}
Ответ
17 грейд
Простое решение - это каждый раз при take делать `System.arraycopy` таким образом как
бы "смещая" очередь к началу. Нужно уточнить у кандидата какая это сложность, и если
он ответит правильно (O(N)), то спросить можно ли это оптимизировать. Если не сможет -
будем считать это ответом на 17 грейд.
Часто кандидаты делают стек, а не очередь. Если кандидат затрудняется сделать именно
очередь, то считаем это ответом на 17 грейд.
18 грейд
Решение должно выглядеть примерно так:
https://matrix.o3.ru/trials 20/23
07.10.2024, 15:50 Задачник
private int writePos = 0;
private int currentSize = 0;
29. Робот
Java Задачи
Есть робот на бесконечной лестнице. У него есть интерфейс. У нас есть ссылка на
интерфейс. В интерфейсе один метод, без аргументов, который возвращает boolean. Если
true — значит он поднялся на одну ступеньку, если false — спустился. Необходимо
написать метод результатом работы которого должно быть то, что робот поднялся на 10
ступенек вверх от своего текущего местоположения.
Ответ
interface Robot {
boolean tryStepUp();
}
oneStepUp(robot);
oneStepUp(robot);
oneStepUp(robot);
oneStepUp(robot);
oneStepUp(robot);
}
Дополнительные вопросы
Ответ
Кандидат должен понимать, что массив - это объект, он состоит из заголовка (для 64ёх
битной jvm это 16 байт), и полей объекта (4 байта на length массива). Итого, это будет (не
учитываем выравнивание и сжатые ссылки):
Т.е. первая структура занимает больше места, т.к. в ней больше объектов (массивов) (1001
VS 11).
31. Thread.State
Java Многопоточность
https://matrix.o3.ru/trials 22/23
07.10.2024, 15:50 Задачник
Ответ
Дополнительные вопросы
Упростите выражение
Ответ
if (!odd || prime) {
https://matrix.o3.ru/trials 23/23