Найти в Дзене

Mutex vs ReadWriteLock. В чём разница?

Оглавление

Когда проводила собеседования, то заметила, что многие говорят именно про Mutex. Я его раньше его не использовала, поэтому стало интересно разобраться.

Что такое Mutex?

Mutex (Mutual Exclusion — взаимное исключение) — это инструмент для синхронизации, который гарантирует, что только один поток будет выполнять определенный участок кода в одно и то же время.

Как устроено: внутри Mutex есть флаг, который указывает, занят ли ресурс или нет. Если поток пытается захватить уже занятый Mutex, он добавляется в некую очередь ожидания. Когда поток, удерживающий блокировку, завершает работу, он освобождает блокировку, устанавливая флаг обратно в какое-то значение и следующий поток из очереди начинает работу.

Пример:

Плюсы:

  1. Простота использования: Mutex интуитивно понятен и в нём не нужно особо разбираться;
  2. Гарантия безопасности: только один поток может войти в критическую секцию. Если мы какой-то кусочек кода поместили в withLock, то в этом кусочке кода всегда будет только один поток, а остальные будут ждать свою очередь.

Минусы:

  1. Нужна дополнительная зависимость implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0'. Я понимаю, что во многих проектах она и так есть, но не везде;
  2. Функция должна быть suspend. Это спорный минус, но я обычно использую такие инструменты при работе с базами данных или префами и там не всегда есть suspend;
  3. Если участок кода заблокирован, то он недоступен даже для чтения;
  4. Потеря производительности: если участок кода часто блокируется, это может стать проблемой (слишком долго ждать).
  5. Deadlocks: если несколько мьютексов захвачены в неправильном порядке, это может привести к взаимной блокировке.
  6. Низкоприоритетный поток может "заблокировать" высокоприоритетный поток, если захватит мьютекс перед ним.

Что такое ReadWriteLock?

ReadWriteLock — это более гибкий инструмент для синхронизации. Внутри ReadWriteLock есть две отдельные блокировки: одна для чтения и одна для записи. Они разрешают множеству потоков читать данные одновременно, но при записи они работают так же, как и Mutex — только один поток может записывать данные.

Примерный алгоритм работы:

  1. Захват на чтение: если нет потоков, которые что-то записывают, и очередь ожидания записывающих потоков пуста, то новый поток может захватить блокировку для чтения. Заметьте, что если уже есть активные читатели, то всё в порядке и новый поток сможет прочитать данные. Счетчик читателей увеличивается. Если сейчас идёт запись, то поток добавляется в ожидающую очередь для чтения.
  2. Захват на запись: поток может захватить блокировку для записи, только если нет активных читателей и писателей. Счетчик писателей увеличивается.
  3. Освобождение: при освобождении блокировки соответствующий счетчик уменьшается. Если счетчик достигает нуля, блокировка освобождается, и ожидающие потоки могут попытаться её захватить.

Пример:

-2

Плюсы:

  1. Повышенная производительность: позволяет множеству потоков одновременно читать данные.
  2. Гибкость: легко адаптировать под различные сценарии.
  3. Блокировка с тайм-аутом: В отличие от обычных мьютексов, ReadWriteLock может дать возможность попытаться захватить блокировку с определенным тайм-аутом.

Минусы:

  1. Сложность: необходимо правильно реализовать механизмы чтения и записи. Тут у нас уже не один withLock на всё. Нужно тщательно продумывать какие методы можно оставить доступными для чтения, а какие блокировать полностью.
  2. Голодание: если чтение происходит значительно чаще записи, существует риск, что потоки, ожидающие записи, будут "голодать", т.е. очень долго ждать своей очереди и когда блокировка снимется. Некоторые реализации решают эту проблему через механизмы приоритетов или "честности" (обрабатываем в том порядке, в каком обращались).
-3

Дубль статей в телеграмме — https://t.me/android_junior

Мои заметки в телеграмме — https://t.me/android_junior_notes

P.S. сделано с помощью ChatGPT и Midjourney. :)