Страницы

Поиск по вопросам

пятница, 14 февраля 2020 г.

Как потоки синхронизируются между собой

#java #многопоточность


Как я понимаю, каждый поток работает в данными которые находятся в кэше на котором
выполняется поток. допустим три поток выполняются в трех ядрах одновременно с одними
и теми же данными. Первый поток делал какие то изменения и завершился. В этом случае
как я понимаю происходит синхронизация данных первого потока со всеми кэшами процессора.
(если конечно я правильно понимаю) 1. Но что если будет конфликт с данными из других
кэшов(как будут мерджиться)? 2. Меня интересует как происходит синхронизация, данные
сбрасываются в ram и говорит другим ядрам типа скачай данные с ram там более актуальные
данные?
    


Ответы

Ответ 1



Ох, это очень сложный вопрос, ответы на который лежат в Java Memory Model и Hardware Memory Model. Последняя может ещё и отличаться для разного оборудования. Прежде всего, завершение потока не гарантирует синхронизации его данных. Данные либо должны иметь модификатор volatile, либо доступ к ним должен осуществляться в пределах синхронизированных блоков. Если совсем кратко описать синхронизированный доступ к данным, то в момент входа потока в синхронизированный блок и выхода из него, процессоры потратят кучу времени на синхронизацию кэшей и памяти. Именно поэтому синхронизацию нельзя использовать бездумно. Если быть чуточку подробнее и рассмотреть железные детали этого процесса, у процессоров есть протокол когерентности кэшей, позволяющий синхронизировать изменения данных в кэшах и не дёргать лишний раз контроллер оперативной памяти. Синхронизация происходит по общей для кэшей всех ядер и контроллера памяти шине. В упрощённом виде (отбросив два из пяти состояний кэшированных данных, а также игнорируя store buffers и invalidation queue) это выглядит так: Процессор №1 делает запрос на блок данных №42. Локальный кэш процессора №1 обнаруживает, что таких данных в нем нет (либо нет вообще, либо они есть в состоянии Invalid). Кэш процессора №1 открывает на шине транзакцию "дайте мне блок данных №42". На запрос отвечает контроллер оперативной памяти пересылая нужный блок. Данные записываются в кэш процессора №1 в состоянии Shared. Процессор №1 изменяет блок данных №42, кэш этого процессора отсылает по шине "я изменил блок данных №42" и устанавливает блоку состояние Modified. Процессор №2 имел в кэше этот блок, он помечает его как Invalid. Процессору №2 понадобился блок №42. Кэш процессора №2 отправляет по шине "дайте мне блок данных №42". Процессор №1 обнаруживает, что у него в кэше этот блок в состоянии Modified, и отправляет по шине "прервать транзакцию" и "запись блока №42 в память". Кэш процессора №2 ждёт завершения транзакции процессора №1. Кэш процессор №2 возобновляет предыдущую транзакцию. Контроллер памяти отдаёт процессору №2 актуальный блок №42 из оперативной памяти. Процессоры №1 и №2 помечают блок №42 в своих кэшах как Shared. Пара статей на Хабре на эту тему - раз и два. А также весьма познавательными могут быть запросы в Google "java memory model", "memory barriers", "memory ordering" и "happens before". Ну, и нельзя в таких вопросах не дать ссылку на Шипилёва.

Комментариев нет:

Отправить комментарий