Многопоточное программирование
Что такое Main Thread?
В Android главный поток обрабатывает события и обновляет UI. Длительные вычисления и блокирующий ввод-вывод выносят с него, чтобы интерфейс оставался отзывчивым и не возникал ANR.
Где вызывать сложные задачи?
Сложные задачи, которые могут занимать много времени или ресурсов, лучше вызывать в отдельных потоках или процессах, чтобы избежать блокировки главного потока и обеспечить отзывчивость пользовательского интерфейса.
Что представляют собой Handler и Looper?
Handler и Looper являются частями механизма обработки
сообщений в Android.
-
Looper обрабатывает очередь сообщений своего потока. Он есть у главного потока Android и может быть создан у фонового потока.
-
Handlerсвязывается с конкретнымLooper’ом и используется для отправки сообщений в очередь сообщений, обрабатываемую этимLooper’ом, а также для планирования выполнения кода в контексте этогоLooper’а.
Handler направляет задачи в поток соответствующего Looper. Сам по себе Handler не переносит работу в фон: Handler с главным Looper выполнит её на UI-потоке.
Как гарантировано вызвать код на главном потоке?
Чтобы гарантированно вызвать код на главном потоке (UI-потоке) в Android, можно использовать Handler с Looper’ом, связанным с главным потоком. Например:
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(() -> {
// Код, который нужно выполнить на главном потоке
});
Этот код гарантирует, что переданный блок выполнится на главном потоке.
Что представляют собой мьютекс, монитор, ключевое слово synchronized и volatile?
Определение терминов:
-
Мьютекс (Mutex): это средство синхронизации, используемое для предотвращения одновременного доступа к общему ресурсу из нескольких потоков. Только один поток может владеть мьютексом в любой момент времени.
-
Монитор: это абстрактное понятие, связанное с синхронизацией потоков. В контексте Java мониторы обычно ассоциируются с объектами. Каждый объект в Java имеет свой монитор, который можно использовать для синхронизации потоков.
-
Ключевое слово synchronized: это ключевое слово в Java, которое используется для синхронизации доступа к методам или блокам кода. Когда метод или блок кода помечен как synchronized, только один поток может выполнить его за раз.
-
Volatile в Java и @Volatile в Kotlin/JVM обеспечивают видимость записи другим потокам и определённые гарантии порядка. Они не делают составные операции вроде count++ атомарными; для этого нужна синхронизация или атомарный тип.
Как управлять потоками в Kotlin и какие классы для этого используются?
В Kotlin для управления потоками можно использовать:
-
Thread: Класс для создания и управления потоками.val thread = Thread { // Код, выполняемый в потоке } thread.start() -
Executors: Для управления пулами потоков.val executor = Executors.newSingleThreadExecutor() executor.execute { // Код, выполняемый в потоке } -
Coroutines: Легковесные потоки, управляемые Kotlin.GlobalScope.launch { // Код, выполняемый в корутине }
Что такое синхронизация и как её реализовать в Kotlin?
Синхронизация управляет доступом к общим изменяемым данным. В примере один и тот же монитор защищает как увеличение счётчика, так и чтение, чтобы результат был виден другим потокам.
Пример:
class Counter {
private var count = 0
@Synchronized
fun increment() {
count++
}
@Synchronized
fun getCount(): Int {
return count
}
}
Объясните разницу между Coroutine и Thread в Kotlin.
Основная разница между Coroutine и Thread:
-
Thread: Поток операционной системы, занимает больше ресурсов. Создание и переключение между потоками требует времени. -
Coroutine: Легковесный поток, управляемый Kotlin. Занимает меньше ресурсов и позволяет проще управлять асинхронными задачами.
Какие проблемы могут возникнуть при работе с многопоточностью и как их избежать?
Основные проблемы при работе с многопоточностью:
-
Гонки данных: Несинхронизированный доступ из нескольких потоков к одной области памяти, когда хотя бы один доступ является записью.
- Решение: Использовать синхронизацию (например,
synchronizedилиReentrantLock).
- Решение: Использовать синхронизацию (например,
-
Взаимоблокировка (
Deadlock): Когда два или более потоков ждут освобождения ресурсов друг от друга.- Решение: Избегать вложенной блокировки, использовать
tryLockдля предотвращения блокировок.
- Решение: Избегать вложенной блокировки, использовать
-
Активная блокировка, или livelock: Потоки продолжают менять состояние, но полезная работа не продвигается.
- Решение: Контролировать и корректировать алгоритмы синхронизации.
-
Избыточное переключение контекста (
Context Switching): Частые переключения между потоками могут снизить производительность.- Решение: Оптимизировать количество потоков, использовать пулы потоков или корутины.
Эти проблемы можно избежать, внимательно проектируя многопоточные приложения и используя соответствующие инструменты синхронизации и управления потоками.