Мы углубимся в режимы потокобезопасности свойства Lazy и поймем разницу между ними.
Свойство Kotlin Lazy позволяет вам создать объект, когда вы вызываете его в первый раз один раз, а затем возвращает сохраненный объект каждый раз, когда вы обращаетесь к этой переменной.
Используя свойство Lazy, вы повысите производительность, потому что
- Объект будет создан только тогда, когда он вам понадобится.
Например, вам может не понадобитьсяfilteredList
, если пользователь не выбрал параметр фильтра на этом экране. - Вы будете повторно использовать инициализированное значение вместо того, чтобы повторно инициализировать его каждый раз, когда оно вам понадобится. Это очень полезно, когда у вас есть тяжелые объекты, такие как БД или огромные списки.
Примечание. Каждое свойство lazy является неизменяемым и не может быть изменено после назначения.
Потокобезопасность
Примечание. Если вы понимаете Thread-Safety, просто пропустите этот раздел.
Thread-Safe означает, что у вас есть предсказуемый код, который может работать в нескольких потоках одновременно без каких-либо условий гонки. Состояние гонки может возникнуть, когда один или несколько потоков одновременно обновляют общий ресурс. Трудно понять это утверждение без примера, поэтому давайте рассмотрим пример :)
Допустим, у нас есть банковское приложение…
Допустим, мы вызвали takePayment()
и takeMonthlyFee()
одновременно в двух разных потоках. Это означает, что порядок кода не будет предсказуемым. Итак, давайте рассмотрим, как код выполняется в следующем порядке.
takePayment(100.0)
balance = 100.0 и paymentAmount = 100if (paymentAmount <= balance)
будет true, balance = 100.0 и paymentAmount = 100takeMonthlyFee()
вызвала balance = 100.0 и paymentAmount = 100balance -= 10
баланс = 90.0 и paymentAmount = 100balance -= paymentAmount
Вот в чем проблема….
Вы видели, чего нам это будет стоить!!!!!! мы возьмем оплату, даже если на балансе клиента недостаточно :) вот что значит иметь состояние гонки.
Нам нужно сделать наши функции Thread-Safe. Таким образом, обычно решение состоит в том, чтобы заблокировать код, использующий общие ресурсы, до тех пор, пока он не будет выполнен, а затем выпустить его.
поэтому, несмотря на то, что takeMonthlyFee()
вызывается после оператора if в нашем примере выше, Kotlin заставит его ждать, пока наш блок synchronized не будет выполнен. Задача решена.
Есть много других решений, позволяющих сделать ваш код Thread-Safe. Вот отличная статья.
Режимы потокобезопасности
По умолчанию свойство Lazy использует режим LazyThreadSafetyMode.SYNCHRONIZED
, но на самом деле свойство Lazy имеет 3 режима, которые вы можете передать в качестве параметра функции lazy.
- СИНХРОНИЗАЦИЯ — многопоточность
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)
Спасибо за чтение, и я надеюсь, что вам понравился блог ❤️