DEV_EASY_NOTES Telegram 336
Недавно на работе возник разговор о Double check locking. Напомню если вдруг забыли. Вот представьте у вас тяжелый объект какой-то, который при создании требует кучу или памяти или времени, и вы не хотите его создавать сразу, а лениво когда понадобится. Мы можем конечно просто взять и вынести его в функцию, и потом вызвать в нужный момент:


private var heavy: HeavyObject? = null

fun get():HeabyObject {
if( heavy == null ){
heavy = HeavyObject()
}
return heavy
}



Однако, что если два потока одновременно вызовут эту функцию? Если класс действительно тяжелый, например инициализация LLM это может быть проблемой. Я уже молчу про остальные проблемы с visibility поля и прочими проблемы с многопоточкой. Тоже мне проблема, ну можно же тупо поставить синхронизацию и все, делов то.


private var heavy: HeavyObject? = null

fun get():HeabyObject = synchronized { … }


Ну вот и все, это решает все возможные проблемы. Правда теперь мы каждый раз используем Mutex даже на чтение, не особо эффективно получается.

Чтобы решить эту проблему, придумали подход называемый Double check locking, который используется в Dagger кстати. Суть проста, давай захватывать Mutex только если мы видим что объект еще не проинициализирован.


@Volatile
private var heavy: HeavyObject? = null

fun get(): HeavyObject {
var result: HeavyObject? = heavy
if (result == null) {
synchronized(lock) {
result = heavy
if (result == null) {
result = HeavyObject()
heavy = result
}
}
}
return result!!
}


Ну понимаете да, сначала чекаем на null, затем захватываем Mutex и еще раз чекаем, ведь кто-то мог захватить Mutex раньше и уже проинициализировать поле. Поэтому и double check.

В целом метод рабочий, хоть и не очень изящный. Однако, в фолиантах истории, есть еще один подход, о котором знают лишь единицы, он доступен только самым достойным (душнилам короче).

Есть у JVM одна интересная особенность, все static блоки выполняются thread safe, т.е JVM их автоматически синхронизирует потому как важно гарантировать что этот блок вызовется лишь один раз. Еще фишка static блоков в том, что они вызываются только в момент обращения к классу. Поэтому мы можем использовать эту систему, чтобы сделать ленивую инициализацию:


private object FieldHolder {

val field: HeavyObject = createObject()

private fun createObject(): HeavyObject = HeavyObject()
}

fun get(): HeavyObject {
return FieldHolder.field
}


Не особо верится, что тут действительно ленивая инициализация или что оно thread safe. Ради эксперимента попробуйте сами это проверить)

Главный вопрос тут конечно, почему тот же Dagger не использует данный подход? Вероятно потому, что если на каждую зависимость будет генериться по классу Holder, а огромное количество классов, которые существуют просто для некоторой оптимизации так себе идея.

Однако в своем коде, если хотите выебнуться на МРе можете такое попробовать. Только потом не плачьте, что с вами на обед не хотят идти. Один дурачок проверял, не будут раскрывать его личность....



tgoop.com/dev_easy_notes/336
Create:
Last Update:

Недавно на работе возник разговор о Double check locking. Напомню если вдруг забыли. Вот представьте у вас тяжелый объект какой-то, который при создании требует кучу или памяти или времени, и вы не хотите его создавать сразу, а лениво когда понадобится. Мы можем конечно просто взять и вынести его в функцию, и потом вызвать в нужный момент:


private var heavy: HeavyObject? = null

fun get():HeabyObject {
if( heavy == null ){
heavy = HeavyObject()
}
return heavy
}



Однако, что если два потока одновременно вызовут эту функцию? Если класс действительно тяжелый, например инициализация LLM это может быть проблемой. Я уже молчу про остальные проблемы с visibility поля и прочими проблемы с многопоточкой. Тоже мне проблема, ну можно же тупо поставить синхронизацию и все, делов то.


private var heavy: HeavyObject? = null

fun get():HeabyObject = synchronized { … }


Ну вот и все, это решает все возможные проблемы. Правда теперь мы каждый раз используем Mutex даже на чтение, не особо эффективно получается.

Чтобы решить эту проблему, придумали подход называемый Double check locking, который используется в Dagger кстати. Суть проста, давай захватывать Mutex только если мы видим что объект еще не проинициализирован.


@Volatile
private var heavy: HeavyObject? = null

fun get(): HeavyObject {
var result: HeavyObject? = heavy
if (result == null) {
synchronized(lock) {
result = heavy
if (result == null) {
result = HeavyObject()
heavy = result
}
}
}
return result!!
}


Ну понимаете да, сначала чекаем на null, затем захватываем Mutex и еще раз чекаем, ведь кто-то мог захватить Mutex раньше и уже проинициализировать поле. Поэтому и double check.

В целом метод рабочий, хоть и не очень изящный. Однако, в фолиантах истории, есть еще один подход, о котором знают лишь единицы, он доступен только самым достойным (душнилам короче).

Есть у JVM одна интересная особенность, все static блоки выполняются thread safe, т.е JVM их автоматически синхронизирует потому как важно гарантировать что этот блок вызовется лишь один раз. Еще фишка static блоков в том, что они вызываются только в момент обращения к классу. Поэтому мы можем использовать эту систему, чтобы сделать ленивую инициализацию:


private object FieldHolder {

val field: HeavyObject = createObject()

private fun createObject(): HeavyObject = HeavyObject()
}

fun get(): HeavyObject {
return FieldHolder.field
}


Не особо верится, что тут действительно ленивая инициализация или что оно thread safe. Ради эксперимента попробуйте сами это проверить)

Главный вопрос тут конечно, почему тот же Dagger не использует данный подход? Вероятно потому, что если на каждую зависимость будет генериться по классу Holder, а огромное количество классов, которые существуют просто для некоторой оптимизации так себе идея.

Однако в своем коде, если хотите выебнуться на МРе можете такое попробовать. Только потом не плачьте, что с вами на обед не хотят идти. Один дурачок проверял, не будут раскрывать его личность....

BY Dev Easy Notes


Share with your friend now:
tgoop.com/dev_easy_notes/336

View MORE
Open in Telegram


Telegram News

Date: |

Polls Hashtags With Bitcoin down 30% in the past week, some crypto traders have taken to Telegram to “voice” their feelings. Activate up to 20 bots While some crypto traders move toward screaming as a coping mechanism, many mental health experts have argued that “scream therapy” is pseudoscience. Scientific research or no, it obviously feels good.
from us


Telegram Dev Easy Notes
FROM American