DEV_EASY_NOTES Telegram 181
{1/2} Хочу рассказать про интересную тему с многопоточностью. И начну с вопроса: что такое синхронизация, если мы говорим про многопоточность?

Синхронизация – способ сделать так, чтобы например метод, который может дёрнуться из нескольких потоков гарантировано вызвался только одним. Остальные потоки в этот момент, должны дождаться пока не закончит первый и т.д по очереди.

Все это можно сделать при помощи такой штуки как Mutex. В Java Mutex реализован на уровне языка и представлен в виде блока synchronized. Все довольно просто, помечаем метод специальным модификатором synchronized и все начинает работать как нужно, потоки будут вызывать метод по очереди. А что если я скажу вам, что есть способ сделать синхронизацию, без блокирования потоков?

Есть такая штука как Atomic переменные. В большинстве случаев про них вспоминают в тот момент, когда нужно быстренько сделать какой-нибудь счетчик, который будет корректно работать в многопоточной системе. Однако это не все кейсы использования Atomic переменных. Диапазон задач, которые можно решить при помощи Atomic гораздо шире, чем просто создание счетчиков.

У всех Atomic переменных есть метод который называется compareAndSet. Что он делает: как очевидно из названия, сравнивает и потом устанавливает значение. Если удалось установить новое значение он возвращает true если не удалось false. С чем он сравнивает? Сравнивает он первый аргумент со значением которое установлено в самом Atomic. Да, на словах звучит стремно, поэтому давайте на примере кода:

val atomic = AtomicBoolean(false)
val newValue = true
val result = atomic.compareAndSet(false, newValue)


Если текущее значение Atomic переменной, совпадает со значением первого аргумента функции compareAndSet, только в этом случае происходит замена на новое значение (newValue).

И казалось бы, ну и что такого крутого, можно и на syncronized блоках сделать точно такой же метод. Не совсем так, под капотом compareAndSet не использует привычные нам инструменты синхронизации, у этого метода нативная реализация.

У каждого современного процессора есть встроенная команда compareAndSet, в литературе часто сокращают и называют CAS. Так вот, команда CAS реализована аж на уровне процессора, означает что этот метод во много раз быстрее чем synchronized блоки.

Как это использовать? Вот самый простой и базовый пример. Представьте, что у вас есть метод, который вызывается из нескольких потоков. И вам нужно сделать так, чтобы поток, который вызывал этот метод первым, сделал один блок кода, а все остальные потоки прошли мимо.

Как это будет сделано с использованием synchronized блока:

var flag = true
fun toDo() = synchronized{
if(flag){
// block
flag = false
}
}


Все отработает как нужно, однако пока один поток будет выполнять метод, все остальные просто будут ждать, как-то не круто. А теперь используем магию Atomic.

var flag = AtomicBoolean(false)
fun toDo() {
if(flag.compareAndSet(false, true)){
// block
}
}


Для данной задачи, даже кода получилось меньше. Что произойдет тут. Метод compareAndSet как вы уже знаете, гарантированно синхронизирован, причем на уровне процессора. Первый поток, который выполняет этот код, успешно произведет замену переменных и метод compareAndSet вернет ему true. Последующие потоки, будут получать результат false, потому как текущее значение уже не совпадает с первым аргументом. Ни один поток не будет заблокирован, все просто пройдут мимо блока if.

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

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



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

{1/2} Хочу рассказать про интересную тему с многопоточностью. И начну с вопроса: что такое синхронизация, если мы говорим про многопоточность?

Синхронизация – способ сделать так, чтобы например метод, который может дёрнуться из нескольких потоков гарантировано вызвался только одним. Остальные потоки в этот момент, должны дождаться пока не закончит первый и т.д по очереди.

Все это можно сделать при помощи такой штуки как Mutex. В Java Mutex реализован на уровне языка и представлен в виде блока synchronized. Все довольно просто, помечаем метод специальным модификатором synchronized и все начинает работать как нужно, потоки будут вызывать метод по очереди. А что если я скажу вам, что есть способ сделать синхронизацию, без блокирования потоков?

Есть такая штука как Atomic переменные. В большинстве случаев про них вспоминают в тот момент, когда нужно быстренько сделать какой-нибудь счетчик, который будет корректно работать в многопоточной системе. Однако это не все кейсы использования Atomic переменных. Диапазон задач, которые можно решить при помощи Atomic гораздо шире, чем просто создание счетчиков.

У всех Atomic переменных есть метод который называется compareAndSet. Что он делает: как очевидно из названия, сравнивает и потом устанавливает значение. Если удалось установить новое значение он возвращает true если не удалось false. С чем он сравнивает? Сравнивает он первый аргумент со значением которое установлено в самом Atomic. Да, на словах звучит стремно, поэтому давайте на примере кода:

val atomic = AtomicBoolean(false)
val newValue = true
val result = atomic.compareAndSet(false, newValue)


Если текущее значение Atomic переменной, совпадает со значением первого аргумента функции compareAndSet, только в этом случае происходит замена на новое значение (newValue).

И казалось бы, ну и что такого крутого, можно и на syncronized блоках сделать точно такой же метод. Не совсем так, под капотом compareAndSet не использует привычные нам инструменты синхронизации, у этого метода нативная реализация.

У каждого современного процессора есть встроенная команда compareAndSet, в литературе часто сокращают и называют CAS. Так вот, команда CAS реализована аж на уровне процессора, означает что этот метод во много раз быстрее чем synchronized блоки.

Как это использовать? Вот самый простой и базовый пример. Представьте, что у вас есть метод, который вызывается из нескольких потоков. И вам нужно сделать так, чтобы поток, который вызывал этот метод первым, сделал один блок кода, а все остальные потоки прошли мимо.

Как это будет сделано с использованием synchronized блока:

var flag = true
fun toDo() = synchronized{
if(flag){
// block
flag = false
}
}


Все отработает как нужно, однако пока один поток будет выполнять метод, все остальные просто будут ждать, как-то не круто. А теперь используем магию Atomic.

var flag = AtomicBoolean(false)
fun toDo() {
if(flag.compareAndSet(false, true)){
// block
}
}


Для данной задачи, даже кода получилось меньше. Что произойдет тут. Метод compareAndSet как вы уже знаете, гарантированно синхронизирован, причем на уровне процессора. Первый поток, который выполняет этот код, успешно произведет замену переменных и метод compareAndSet вернет ему true. Последующие потоки, будут получать результат false, потому как текущее значение уже не совпадает с первым аргументом. Ни один поток не будет заблокирован, все просто пройдут мимо блока if.

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

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

BY Dev Easy Notes


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

View MORE
Open in Telegram


Telegram News

Date: |

Some Telegram Channels content management tips A Telegram channel is used for various purposes, from sharing helpful content to implementing a business strategy. In addition, you can use your channel to build and improve your company image, boost your sales, make profits, enhance customer loyalty, and more. A Hong Kong protester with a petrol bomb. File photo: Dylan Hollingsworth/HKFP. To view your bio, click the Menu icon and select “View channel info.” The administrator of a telegram group, "Suck Channel," was sentenced to six years and six months in prison for seven counts of incitement yesterday.
from us


Telegram Dev Easy Notes
FROM American