Мы углубимся в режимы потокобезопасности свойства Lazy и поймем разницу между ними.

Свойство Kotlin Lazy позволяет вам создать объект, когда вы вызываете его в первый раз один раз, а затем возвращает сохраненный объект каждый раз, когда вы обращаетесь к этой переменной.

Используя свойство Lazy, вы повысите производительность, потому что

  1. Объект будет создан только тогда, когда он вам понадобится.
    Например, вам может не понадобиться filteredList, если пользователь не выбрал параметр фильтра на этом экране.
  2. Вы будете повторно использовать инициализированное значение вместо того, чтобы повторно инициализировать его каждый раз, когда оно вам понадобится. Это очень полезно, когда у вас есть тяжелые объекты, такие как БД или огромные списки.

Примечание. Каждое свойство lazy является неизменяемым и не может быть изменено после назначения.

Потокобезопасность

Примечание. Если вы понимаете Thread-Safety, просто пропустите этот раздел.

Thread-Safe означает, что у вас есть предсказуемый код, который может работать в нескольких потоках одновременно без каких-либо условий гонки. Состояние гонки может возникнуть, когда один или несколько потоков одновременно обновляют общий ресурс. Трудно понять это утверждение без примера, поэтому давайте рассмотрим пример :)

Допустим, у нас есть банковское приложение…

Допустим, мы вызвали takePayment() и takeMonthlyFee() одновременно в двух разных потоках. Это означает, что порядок кода не будет предсказуемым. Итак, давайте рассмотрим, как код выполняется в следующем порядке.

  1. takePayment(100.0)balance = 100.0 и paymentAmount = 100
  2. if (paymentAmount <= balance) будет true, balance = 100.0 и paymentAmount = 100
  3. takeMonthlyFee() вызвала balance = 100.0 и paymentAmount = 100
  4. balance -= 10 баланс = 90.0 и paymentAmount = 100
  5. balance -= paymentAmount Вот в чем проблема….

Вы видели, чего нам это будет стоить!!!!!! мы возьмем оплату, даже если на балансе клиента недостаточно :) вот что значит иметь состояние гонки.

Нам нужно сделать наши функции Thread-Safe. Таким образом, обычно решение состоит в том, чтобы заблокировать код, использующий общие ресурсы, до тех пор, пока он не будет выполнен, а затем выпустить его.

поэтому, несмотря на то, что takeMonthlyFee() вызывается после оператора if в нашем примере выше, Kotlin заставит его ждать, пока наш блок synchronized не будет выполнен. Задача решена.

Есть много других решений, позволяющих сделать ваш код Thread-Safe. Вот отличная статья.

Режимы потокобезопасности

По умолчанию свойство Lazy использует режим LazyThreadSafetyMode.SYNCHRONIZED, но на самом деле свойство Lazy имеет 3 режима, которые вы можете передать в качестве параметра функции lazy.

  1. СИНХРОНИЗАЦИЯ — многопоточность
val value by lazy(LazyThreadSafetyMode.SYNCHRONIZED){ ... }

Лямбда-инициализатор вызывается только один раз (Kotlin гарантирует это, блокируя инициализатор при первом вызове), поэтому значение вычисляется только один раз в одном потоке, но все потоки будут использовать сохраненное значение.

2. ПУБЛИКАЦИЯ — потокобезопасность

val value by lazy(LazyThreadSafetyMode.PUBLICATION)

Лямбда-инициализатор вызывается в каждом потоке, но только первое возвращаемое значение будет использоваться как значение Lazy instance, а все потоки будут использовать сохраненное значение.

Примечание. Я не могу придумать другого варианта использования этого режима, кроме ведения журнала. Если вы знаете какие-либо, добавьте их в комментарии.

3.НЕТ – без многопоточности

val value by lazy(LazyThreadSafetyMode.NONE)

Вы можете использовать этот режим, чтобы избежать всех этих накладных расходов и сделать его Thread-Safe.

Будьте осторожны при использовании этого режима, поскольку для синхронизации доступа к значению экземпляра Lazy не используются блокировки; если к экземпляру обращаются из нескольких потоков, его поведение не определено.

Лучший вариант использования — это когда вы уверены, что используете только один поток, такой как MainThread в Android.

Совет. Чтобы упростить себе жизнь, вы можете использовать функцию, которая возвращает ленивое свойство с режимом NONE.

fun <T> fastLazy(initializer: () -> T): Lazy<T> =    lazy(LazyThreadSafetyMode.NONE, initializer)

Спасибо за чтение, и я надеюсь, что вам понравился блог ❤️