Пример для сложности O(N2)
Цикл в цикле.
Как организовать рекурсию?
Вызвать самого себя в теле.
Чем отличаются функциональные и нефункциональные требования?
Функциональные требования описывают, что система должна делать, какие функции выполнять и как она должна вести себя в различных сценариях. Это конкретные действия и процессы, которые система поддерживает.
Например, "система должна позволять пользователю входить в личный кабинет".
Нефункциональные требования описывают, как система должна выполнять свои функции. Они касаются качества работы системы и охватывают такие аспекты, как производительность, безопасность, удобство использования и надежность.
Например, "система должна обрабатывать до 1000 запросов в секунду".
Таким образом, функциональные требования связаны с поведением системы, а нефункциональные — с характеристиками этого поведения.
Что такое пауза GC
Пауза GC это когда сборщик мусора останавливает работу приложения и собирает мусор.
Их длина завсит от реализации GC.
Худший случай: остановили, "пометили" мусор, собрали, а только потом отпустили. Долго.
Оптимизированный: остановили, быстро выяснили, где искать мусор, отпустили и параллельно "метим" мусор (а точнее живое), как пометили, остановили не надолго, параллельно запустили процесс переноса живых данных, как перенесли, пометили сектора (откуда брали) как пустые и можем снова в них писать.
Какая сложность алгоритма есть в коллекциях Map?
HashMap - операции добавления, удаления и поиска обычно имеют амортизированную сложность O(1), но в худшем случае могут быть O(n), если произошло множество коллизий и структура данных становится связным списком.
TreeMap - основана на красно-черном дереве и обеспечивает логарифмическую сложность O(log n) для основных операций добавления, удаления и поиска.
LinkedHashMap - имеет амортизированную сложность O(1) для операций добавления, удаления и поиска, с той же уязвимостью к худшему сценарию O(n) при большом количестве коллизий.
Какой простейший код может нам выдать ошибку StackOverflow?
Бесконечная рекурсия
В чем различие между методами Stream API map и flatMap?
map применяется для преобразования каждого элемента стрима в другой элемент. Возвращает стрим с результатами преобразования.
flatMap берет каждый элемент из исходного стрима и применяет к нему функцию, которая возвращает новый стрим. Затем flatMap объединяет все эти промежуточные стримы в один итоговый стрим и возвращает его.
Как можно получить уникальные элементы из коллекции с использованием Stream API?
Самый простой способ использовать команду DISTINCT
Для извращенцев - можно сколектить в Set.
Что такое стартеры в Spring Boot?
В Spring Boot стартерами называются специальные Maven или Gradle зависимости, которые содержат в
себе набор библиотек и настроек для быстрого и простого подключения функциональности к
приложению.
Они упрощают настройку зависимости для распространённых случаев использования.
Например, spring-boot-starter-web включает все необходимые библиотеки для создания
веб-приложения с использованием Spring MVC.
Какие вы знаете методы создания бинов?
Некоторые из основных методов:
Аннотация @Component и связанные аннотации:
@Component public classMyComponent { // Bean definition }
Конфигурационные классы и аннотация @Bean:
Бины могут быть созданы с использованием методов, аннотированных как
@Configuration public classAppConfig {@Bean public MyBeanmyBean () { return new MyBean(); } }
XML-конфигурация:
Бины также могут быть определены в XML-файлах конфигурации с использованием
<bean> элементов.
<bean id="myBean" class="com.example.MyBean"/>
Какие вы знаете методы внедрения бинов?
В Spring есть несколько способов внедрения бинов (инъекции зависимостей):
Конструкторная инъекция
Зависимости передаются через параметры конструктора.
Это позволяет установить все необходимые зависимости сразу
при создании экземпляра класса и делает объект неизменяемым после его создания.
public classMyService { private final Dependencydependency ;@Autowired publicMyService (Dependency dependency) { this.dependency = dependency; } }
Сеттерная инъекция
Зависимости передаются через сеттеры (методы установки).
Это позволяет изменять зависимости после создания объекта,
но требует дополнительного кода для проверки, что все зависимости установлены.
public classMyService { private Dependencydependency ;@Autowired public voidsetDependency (Dependency dependency) { this.dependency = dependency; } }
Инъекция через поле
Поля зависимостей помечаются аннотацией @Autowired.
Это самый простой способ внедрения, но менее предпочтительный,
так как нарушает инкапсуляцию и затрудняет тестирование.
public classMyService {@Autowired private Dependencydependency ; }
Как вы можете использовать @Version аннотацию в Spring Data JPA для обработки конкурентного доступа?
Только не понятно как это используется....
Чем отличаются стратегии @GeneratedValue JPA: SEQUENCE, IDENTITY и AUTO?
SEQUENCE: Использует последовательность для генерации уникальных значений. Это рекомендуется использовать в базах данных, поддерживающих последовательности (например, Postgres, Oracle).
IDENTITY: Делегирует генерацию ключей самой базе данных, обычно используется в базах данных, поддерживающих автоинкрементные столбцы (например, MySQL).
AUTO: Стратегия выбирается JPA провайдером автоматически, основываясь на специфике используемой базы данных.
Разница между потоком и процессом?
Процесс
Это независимая программная сущность, которая располагает своим собственным адресным
пространством и системными ресурсами. Процессы изолированы друг от друга и только
взаимодействуют через межпроцессное взаимодействие (IPC).
Поток
Это более легковесная единица выполнения, которая существует внутри процесса и
использует общее адресное пространство процесса. Потоки могут совместно использовать память
и ресурсы процесса, что делает переключение контекста между потоками более быстрым по
сравнению с процессами.
Что такое ThreadLocal переменная?
ThreadLocal
- это специальный вид переменной в Java, который позволяет каждому потоку иметь
собственное, независимое значение этой переменной.
То есть, даже если несколько потоков обращаются к одной и той же ThreadLocal переменной,
на самом деле каждый поток работает со своим собственным экземпляром этой переменной.
Основные особенности ThreadLocal:
Изолирование данных
Каждый поток работает со своим значением, что устраняет необходимость в
синхронизации доступа к данным между потоками. Это делает ThreadLocal полезным для
хранения данных, связанных с текущим потоком, например, пользовательской сессии или
транзакции.
Производительность
Поскольку каждый поток работает с собственным экземпляром переменной, нет
контенциональности между потоками при доступе к ThreadLocal, что может улучшить
производительность в многопоточных приложениях.
Жизненный цикл
Значения, хранимые в ThreadLocal, существуют в течение всего времени жизни
потока. Это позволяет использовать ThreadLocal для хранения информации,
которая актуальна в контексте выполнения одного потока.
Чем отличается метод satrt() от run()
start()
Метод, который запускает новый поток выполнения.
Он создает новый системный поток и вызывает его метод run().
Использование start() позволяет вашему коду
выполняться параллельно с другими потоками.
run()
Метод, содержащий код для выполнения в потоке.
Если вызвать непосредственно run(),
он выполнится в текущем потоке, а не в новом потоке.
Таким образом, основное различие заключается в том, что start() создает новый поток, тогда как run() просто выполняет метод в текущем потоке.
Каким образом можно синхронизировать доступ к общей коллекции в JAVA?
Java доступ к общей коллекции можно синхронизировать несколькими способами:
Collections
Обернуть коллекцию в синхронизированную версию
Использование потокобезопасных коллекций
Например, ConcurrentHashMap, CopyOnWriteArrayList, и другие классы из пакета java.util.concurrent, которые обеспечивают встроенную синхронизацию.
Явная синхронизация Использовать блоки synchronized в коде доступа к коллекции
Locks
(из java.util.concurrent.locks)
Использовать явные замки, такие как ReentrantLock,
для управления доступом к коллекции.
У нас есть какая-то переменная i. Она volatile, она равняется нулю. Я в двух потоках делаю инкременты этой переменной. Это будет потокобезопасно?
Ожидается ответ - нет.
Но если делать инкрементацию в синхронизированном методе - то да.
А лучще всего использовать Atomic-классы.
Почему в данном случае Atomic более предпочтительно использовать для счетчиков?
Тут надо сказать - в многопоточной среде.
когда необходимо часто обновлять значение переменной (не обязательно счетчика) из разных потоков.
Причины:
Безопасность (в многопоточности)
Классы из пакета java.util.concurrent.atomic обеспечивают атомарные
операции, что означает, что операции инкрементации и декрементации
выполняются за одну неделимую операцию.
Это предотвращает проблемы, такие как "гоночные" состояния, без необходимости
использовать явные блокировки (synchronized).
Производительность
Использование атомарных классов может быть более производительным,
чем явные блокировки, такие как synchronized, поскольку они не блокируют потоки.
Вместо этого, они полагаются на низкоуровневые атомарные инструкции процессора,
такие как CAS(Compare-And-Swap).
Простота использования
Атомарные классы предоставляют простые методы для выполнения атомарных
операций, такие как incrementAndGet(), decrementAndGet(), и т.д.,
что делает код более читаемым и менее подверженным ошибкам.
Разница между TRUNCATE и DELETE
TRUNCATE
Эта команда используется для удаления всех строк из таблицы. Она выполняется без
логирования отдельных удалений, что делает её более быстрой и менее ресурсоёмкой.
TRUNCATE не может быть откатом в некоторых системах баз данных, так как это DDL (Data
Definition Language) операция.
DELETE
Используется для удаления выбранных строк из таблицы. Это DML (Data Manipulation
Language) операция, и каждое удаление логируется, что позволяет использовать транзакции для
отката изменений, если это необходимо.
Основные различия
TRUNCATE быстрее и не учитывает индивидуальные строки, тогда как DELETE
позволяет больше контроля, удаляя отдельные записи и поддерживая механизм отката.
Что такое шардирование?
Шардирование (от англ. "sharding") - это метод разделения базы данных или другой системы на более мелкие, управляемые части, которые называются "шардами" (shards). Каждая шарда — это подмножество данных, которое может быть размещено и обработано отдельно, обычно на разных серверах или узлах.
Цель шардирования - улучшить производительность и масштабируемость системы. Оно позволяет распределить нагрузку на несколько серверов, что в свою очередь может сделать систему более устойчивой к отказам. Если компактно выражаться, шардирование помогает обрабатывать большие объемы данных более эффективно, обеспечивая параллельную обработку запросов. Часто шардирование применяют в распределённых системах и масштабируемых веб-приложениях, где требуется обработка больших объёмов данных с высокой скоростью.
Основные принципы шардирования:
Чёткое определение логики разделения данных:
нужно четко решить по какому критерию данные будут
распределены между шардами
(например, по какому-то идентификатору, временному интервалу и т.д.).
Изоляция:
каждая шарда автономна и может работать независимо от других, хотя данные в шардах
часто всё равно нуждаются в согласованности.
Горизонтальное масштабирование:
добавление нового шарда позволяет увеличить ёмкость системы без
значительных изменений в архитектуре.
Что такое реплицирование?
Реплицирование - это процесс создания и поддержания копий данных на нескольких серверах или узлах в системе.
Основная цель репликации - обеспечить отказоустойчивость, повысить доступность данных и улучшить производительность системы.
При репликации одни и те же данные хранятся в нескольких местах, что позволяет системе оставаться функциональной даже при выходе из строя одного или нескольких узлов.
Существует несколько моделей репликации:
Синхронная репликация:
при внесении изменений в данные на одном узле эти изменения сразу же
применяются ко всем копиям данных на других узлах.
Это гарантирует, что все копии данных остаются консистентными, но может увеличить
задержки при операциях записи из-за необходимости дожидаться подтверждения от всех
узлов.
Асинхронная репликация:
изменения на основном узле передаются на реплики с задержкой.
Это может улучшить производительность, так как узел не должен ждать подтверждения от
реплик при записи, но есть риск того, что данные на репликах временно могут быть
неактуальными.
Репликация помогает обеспечить:
Отказоустойчивость:
если один сервер выходит из строя, данные могут быть доступны с другой реплики.
Доступность:
при высокой нагрузке запросы могут распределяться между разными репликами, что
снижает нагрузку на каждый отдельный узел.
Географическое распределение данных:
хранение реплик данных в разных географических локациях
позволяет пользователям получать доступ к данным с меньшими задержками.
Добавление механизма репликации делает систему более сложной с точки зрения управления консистентностью данных, особенно в случае асинхронной репликации.
Что такое Testcontainers?
Testcontainers — это библиотека для написания тестов в JAVA, которая позволяет запускать тесты с использованием изолированных контейнеров Docker. Она помогает создавать тестовые среды, которые могут включать базы данных, очереди сообщений и другие сервисы, необходимые для тестирования.
Это позволяет тестам быть более надежными и предсказуемыми, поскольку каждый тест может работать в своей изолированной и чистой среде, созданной с использованием Docker-контейнеров.
Какие гарантии доставки есть в Kafka?
At Least Once - событие точно будет доставлено хотя бы один раз, но может и больше.
At Most Once - событие будет доставлено один раз, но может не быть доставлено вообще.
Exactly Once - событие будет доставлено строго один раз.
Дубликаты | Потерянные сообщения | Обработка | |
---|---|---|---|
At Least Once | да | нет | 1 или много раз |
At Most Once | нет | да | 0 или 1 раз |
Exactly Once | нет | нет | 1 раз |
Как вы можете реализовать идемпотентность при обработке сообщений в Spring Boot Kafka, и какие сложности и ограничения могут возникнуть?
Идемпотентность при обработке сообщений в Spring Boot Kafka можно реализовать несколькими способами:
Использование уникальных идентификаторов сообщений
Включение уникальных идентификаторов в сообщения и хранение информации
о уже обработанных идентификаторах, что позволяет системе обрабатывать одно
и то же сообщение только один раз.
Использование внешнего хранилища idempotent keys
Это может быть база данных или распределенное кеширование,
где хранятся идентификаторы уже обработанных сообщений.
Транзакционная обработка
Исполнение операций в рамках транзакций, обеспечивая,
что сообщения обрабатываются только если все операции внутри транзакции успешны.
Сложности и ограничения:
- Хранилище для отслеживания обработанных сообщений должно быть быстрым и надежным, чтобы не стать узким местом.
- При выборе внешнего хранилища необходимо учитывать производительность и возможное время задержки.
- Управление состоянием может быть сложным в распределенных системах, требуя дополнительного внимания к безопасности данных и устойчивости к сбоям.
- В случае необходимого поддержания большого количества идентификаторов сообщений, это может стать проблемой в плане потребления ресурсов, например, памяти.