Введение в многопоточность 0:00 Многопоточность важна для разработки приложений под Android. Лекция охватывает базовые понятия многопоточности, процессы и потоки. Будут рассмотрены механизмы работы с потоками и инструменты из Java и Kotlin.
Основы многопоточности 1:57 Многопоточность позволяет процессору выполнять несколько потоков одновременно. Процесс — это экземпляр программы, а поток — способ выполнения процесса. В Android процессы и потоки работают в общей памяти и данных.
Иерархия процессов и потоков 2:56 В Linux процессы имеют уникальные идентификаторы и адресное пространство. Потоки живут в каждом процессе и делят общую память. Многоядерные устройства могут работать с разными потоками одного процесса.
Переключение контекста 3:55 Потоки выполняются поочередно, что требует переключения контекста. Это влияет на эффективность многопоточной программы. Термин "concurrency" описывает переключение контекста.
Зачем нужна многопоточность 4:54 Однопоточные программы проще разрабатывать и поддерживать. Они могут иметь недостатки при обработке больших объемов данных и блокирующих задач. В Android многопоточность позволяет эффективно использовать ресурсы устройства.
Пример использования многопоточности 6:52 MainThread отвечает за рендеринг UI и запуск основных компонентов. Фоновые потоки помогают разгрузить MainThread и повысить производительность. Многопоточность позволяет использовать все ядра устройства и создавать более эффективные приложения.
Работа с потоками в Kotlin 9:49 Потоки в Kotlin представлены классом Thread. Для создания потока нужно передать логику и намерение. Поток завершает работу после выхода из функции.
Жизненный цикл потока 12:46 Поток может находиться в состояниях New, Runnable, Blocked и Dead. Важно понимать эти состояния для управления потоками.
Остановка потока 13:46 Поток блокируется, если он засыпает или ждет отмашки. Поток переходит в состояние терминации после завершения работы или исключения. Основной способ остановки потока — функция interupt, но она не всегда работает корректно.
Кооперативная отмена потоков 14:46 Отмена потоков в Android кооперативна, что позволяет завершать их корректно. Принудительное завершение потока небезопасно, так как не известно его текущее состояние. Поток должен быть готов к отмене и проверять статус отмены.
Проверка статуса отмены 15:43 Поток может проверять статус отмены с помощью флага interrupted. Если поток отменен, он завершает работу и очищает ресурсы. Другой способ — использовать режим ожидания и вызывать interupt для завершения потока.
Ожидание выполнения потока 18:37 Функция join блокирует текущий поток до завершения другого потока. В примере поток засыпает на 2 секунды и печатает строку, а основной поток ждет его завершения. Не рекомендуется использовать join для ожидания выполнения потоков в Android.
Создание и ожидание нескольких потоков 19:35 Создание нескольких потоков и их совместное ожидание. Поток, который завершил работу, не блокирует основной поток. Порядок вызова join не важен, так как ожидание выполняется до завершения наибольшего потока.
Потоки с общими данными 21:33 Потоки могут работать с общими данными, что усложняет предсказуемость результатов. Возможные результаты: 01, 10, 11, в зависимости от порядка выполнения потоков. Оптимизации компилятора могут изменить порядок выполнения кода, что влияет на результаты.
Оптимизации компилятора 24:28 Компилятор может менять порядок инструкций для оптимизации. Это может привести к неожиданным результатам при взаимодействии потоков. Важно предугадывать результаты и делать потоки безопасными.
Видимость изменений памяти 26:23 Потоки могут видеть разные значения переменных из-за кэширования. В основной памяти могут быть нули, а в кэшах потоков — единички. Это может повлиять на результаты выполнения программы.
Введение в Java Memory Model 27:22 Java Memory Model обеспечивает корректное поведение многопоточного кода. Она делает легальными оптимизации компилятора и процессора. Модель описывает отношения между высокоуровневым кодом и памятью.
Синхронайз блок и критическая секция 29:19 Синхронайз блок защищает критическую секцию кода. Потоки, пытающиеся получить доступ к занятой секции, блокируются. Пример с ключами и дверью помогает понять принцип работы.
Использование мьютикса 31:18 Мьютикс обеспечивает взаимное исключение для доступа к секции. Мьютикс прикреплен к каждому объекту в JVM. Мьютикс гарантирует, что только один поток может использовать секцию одновременно.
Применение мьютикса для чтения и записи 32:17 Синхронайз блок используется как для чтения, так и для записи. Пример с переменными А и Б показывает, как мьютикс гарантирует порядок выполнения потоков. Использование мьютикса позволяет оптимизировать код.
Свойство reentrancy 35:16 Reentrancy позволяет повторно входить в критическую секцию. Это предотвращает самоблокировку и позволяет избежать дедлоков. Понимание работы мьютикса важно для понимания работы потоков.
Пример с интерфейсами Observer и Observer 38:13 Пример с интерфейсами Observer и Observer для Android разработчиков. Небезопасная реализация через сет приводит к ошибкам в многопоточной среде. Безопасная реализация с использованием мьютикса решает проблему.
Проблемы с оповещением 41:07 Основная проблема с оповещением заключается в том, что веб-серверы могут выполнять дополнительную логику и блокироваться. Это может привести к дедлоку, если несколько потоков пытаются дождаться доступа к критической секции. Решение: создание копии текущего сета и работа с ней для предотвращения ошибок.
Примеры использования синхронных секций 42:07 Пример из Android SDK: регистрация лай сайклбэков использует синхронные секции для безопасного доступа к коллекциям. Пример из Java: использование класса Synchronized для безопасного доступа к коллекциям. Пример из Kotlin: использование делегата Lazy для безопасного доступа к данным.
Использование volatile переменных 45:05 volatile переменные гарантируют видимость изменений в других потоках. Они не обеспечивают атомарность изменений, но полезны для случаев, когда значение переменной просто меняется. Пример: использование volatile переменных в классе CopyOnWriteArrayList для безопасного изменения массива.
Вопросы и ответы 48:00 Могут ли быть процессы без потоков или потоки без процессов? Ответ: в Android разработке всегда будет существовать процесс и поток. Польза многопоточности на одноядерных устройствах: переключение контекста для выполнения полезной нагрузки. Как лучше запускать потоки: использование статической функции из Kotlin для создания и запуска потока.
Синхронные секции и объекты 51:54 Синхронные секции позволяют изолировать работу с общими ресурсами. Использование примитивов для синхронизации может привести к багам, лучше использовать изолированные объекты. Пример из Telegram: использование строки или int для синхронизации приводило к багам.
Разделяемые ресурсы и синхронизация 54:50 Доступ к разделяемым ресурсам должен быть синхронизирован, чтобы избежать конфликтов. Пример с комнатой: доступ к полезной нагрузке возможен только при наличии ключей. В Kotlin синхронизация реализована через свойство @Synchronized, что может привести к ошибкам при неправильном использовании.
Преимущества и недостатки @Synchronized 55:49 Использование @Synchronized может привести к неявному поведению, что требует внимательного ревью кода. @Synchronized помогает избежать проблем с многопоточностью, передавая данные в общую память. Пример с CopyOnWriteArrayList: @Synchronized обеспечивает гарантии безопасности в многопоточной среде.
Интерфейс Lock и его использование 58:43 Интерфейс Lock предоставляет расширенные возможности работы с критическими секциями. Пример использования: проверка занятости Lock и выполнение альтернативных действий при блокировке. Реализация ReadWriteLock позволяет оптимизировать работу с критическими секциями для чтения и записи.
Пример использования ReadWriteLock 1:03:41 ReadWriteLock позволяет оптимизировать доступ к критическим секциям для чтения и записи. Пример с серверами: использование Lock для записи и чтения для оптимизации работы с данными. ReadWriteLock позволяет избежать блокировок при чтении данных, когда запись уже идет.
Безопасный доступ к переменным 1:05:38 Пример с счетчиком: создание 100 потоков и инкрементирование переменной. Проблема: неатомарность операций чтения, увеличения и записи, что может привести к несогласованности данных. Решение: использование классов с префиксом Atomic для обеспечения атомарности операций.
Atomic Integer 1:08:35 Atomic Integer позволяет атомарно изменять данные, например, увеличивать счетчик. В функции инкремент увеличивается счетчик на единицу и возвращается текущее значение. Использование Atomic Integer решает проблему с инкрементом счетчика до 100 тысяч.
Применение Atomic 1:09:35 Atomic используется в продакшн-коде для атомарного изменения данных. В команде часто используются Atomic Reference и Atomic Long. Atomic использует неблокирующие операции доступа к переменным.
Синхронизаторы и неблокирующие коллекции 1:10:34 Синхронизаторы, такие как команда, барьер и семафор, используются для сложных кейсов. Неблокирующие коллекции, такие как CopyOnWriteArrayList и ConcurrentHashMap, позволяют использовать неблокирующий контракт. Эти коллекции используют сложные алгоритмы и математику для работы с многопоточным кодом.
Утилита для изучения синхронизаторов 1:13:32 Интерактивная утилита позволяет изучать работу синхронизаторов и коллекций. Помогает понять, как эти классы ведут себя в различных ситуациях. Рекомендуется попробовать собрать и использовать эту утилиту.
Executor и Future 1:14:30 Executor позволяет абстрагировать передачу задач на выполнение. Future возвращает объект, который можно использовать для взаимодействия с результатом задачи. Executor сервисы работают с набором потоков и имеют разные контракты.
Работа с потоками в Android 1:18:25 В Android используется собственный механизм взаимодействия между потоками. MainThread отвечает за основные функции приложения, такие как отрисовка и обработка действий. MessageQueue, Looper и Handler используются для обработки сообщений и взаимодействия между потоками.
Обработка сообщений в Android 1:22:17 Сообщения обрабатываются в общем списке, который формируется с использованием неограниченного по размеру направленного связанного списка. Фоновые потоки вставляют сообщения в очередь, которые могут быть переданы для обработки и хранить таймстеп для выполнения. Текущий лупер берет сообщения и передает их нужному хендлеру для обработки.
Использование хендлеров 1:23:15 Хендлер требует передачи лупера для работы. Рекомендуется использовать конструктор с передачей лупера, а не пустой конструктор. Хендлер может быть привязан к текущему луперу для отправки сообщений на main thread.
Проблемы с утечкой памяти 1:25:14 Использование хендлеров для отправки задач может привести к утечке памяти. Это происходит из-за хранения ссылок на фрагменты или активности в mess HQ. Метод removeCallbacks помогает избежать утечек памяти.
Отправка сообщений на main thread 1:26:14 Можно использовать runOnMainThread для отправки задач на main thread. Activity и View также имеют методы для отправки сообщений. Хендлеры можно использовать для отправки задач на кастомные потоки.
Создание кастомных потоков 1:28:10 Можно создать кастомный worker thread на основе обычного потока. Для этого нужно подготовить лупер и привязать хендлер к текущему луперу. Это позволяет организовать выполнение задач в кастомном потоке.
Практическое применение 1:30:08 Пример использования API для получения данных из бэкенда. Логика работы с данными выносится на бэкграунд поток. Использование worker thread для делегирования задач на исполнение.
Отмена задач 1:33:04 Для отмены задач используется executor. В onDestroy можно отменить текущую задачу. Важно проверять кооперативность отмены задач.
Параллельные запросы 1:35:01 Запросы можно распараллелить и контролировать их выполнение. В onDestroy нужно завершать все ненужные задачи. Пример показывает, как можно оптимизировать и контролировать выполнение задач.
Обработка ошибок в многопоточном коде 1:36:00 Необходимо уметь обрабатывать ошибки при получении данных из сети. Пример через trackege показывает, как это может выглядеть на практике. Даже простая задача с объединением результатов и отображением на экране становится сложной.
Современные подходы к многопоточности 1:36:58 В компаниях используются различные утилиты и классы для работы с многопоточным кодом. Google часто использует концепции LiveData для работы с сетевым слоем. В последние годы наблюдается переход к использованию корутин, что станет темой следующей лекции.
Основные концепции многопоточности 1:38:57 Обсуждены основы работы с процессами, потоками, разделяемыми ресурсами и базовыми подходами в многопоточной среде. В Android многопоточность включает работу с Handler и MyHandler. Рекомендованы книги и ресурсы для углубленного изучения темы.
Вопросы и ответы 1:40:54 Объяснение разницы между синхронной секцией и лок. Различие между параллельностью и асинхронностью. Рекомендации по использованию Handler в моделях и активах. Рекомендованные книги и статьи для расширения знаний по теме.