В практической ситуации поток записал данные в обычные поля, затем передал задачу через ExecutorService.submit: какую гарантию видимости получает выполняющая её задача?
Действия потока, выполненные до вызова ExecutorService.submit, происходят до действий переданной задачи. Поэтому задача обязана увидеть результаты этих записей, даже если поля не объявлены как volatile и не защищены отдельной блокировкой.
Гарантия относится только к действиям до submit. Записи, выполненные после передачи задачи, этой гарантией не покрываются.
ExecutorService отделяет отправку работы от управления конкретными потоками. Задача может попасть в очередь и быть выполнена уже существующим рабочим потоком, поэтому простой расчёт на момент создания потока здесь неприменим.
Для безопасной передачи работы Java определяет memory-consistency guarantee: действия до отправки задачи происходят до начала её выполнения. Это решает проблему видимости данных при повторном использовании потоков из пула.
Обычная запись в поле сама по себе не гарантирует, что другой поток немедленно увидит новое значение. Без отношения happens-before другой поток может наблюдать устаревшее значение или состояние, не согласованное с ожиданиями программы.
При передаче задачи через исполнитель возникает именно такая граница взаимодействия. Если разработчик не понимает её семантику, он может либо ошибочно добавить лишнюю синхронизацию, либо, наоборот, решить, что любые последующие изменения автоматически станут видимыми задаче.
Спецификация ExecutorService устанавливает отношение: действия потока до помещения задачи в исполнитель происходят до действий задачи. Следовательно, записи в обычные поля, выполненные до submit, становятся видимыми потоку-исполнителю при выполнении этой задачи.
В примере задача должна увидеть значение 42: вызов submit создаёт необходимую границу публикации. Это не означает, что поле становится потокобезопасным для всех последующих чтений и записей.
Если после submit исходный поток изменит value, задача не получает гарантии увидеть именно это изменение. Для такого взаимодействия нужны отдельные средства: volatile, блокировка, атомарные классы, передача результата через Future или другие средства с установленными отношениями happens-before.
Гарантия относится к действиям отправляющего потока, а не к любым действиям всех потоков, которые когда-либо работали с объектом. Если данные были изменены другим потоком и отправляющий поток не синхронизировался с ним, одного submit недостаточно для публикации тех изменений.
Сервис формировал неизменяемую конфигурацию и передавал задачи в общий пул. Разработчик добавил volatile ко всем полям конфигурации, хотя объект полностью заполнялся до submit и после публикации не изменялся.
Рассматривались два варианта. Сделать каждое поле volatile повышало заметность намерения, но усложняло модель и не решало бы проблему составных изменений. Использовать блокировку при каждом чтении обеспечивало бы синхронизацию, но добавляло ненужные издержки.
Выбрали публикацию полностью подготовленного объекта через submit, не изменяя его после отправки. Это опиралось на гарантированный переход видимости, уменьшило синхронизацию и сохранило простой жизненный цикл данных. Если бы объект продолжал изменяться, потребовался бы отдельный протокол синхронизации.
Нет, не автоматически. Гарантия начинается с действий потока, вызывающего submit; она не распространяется назад через несинхронизированное взаимодействие с другим потоком.
Если поток A изменил данные, а поток B затем вызвал submit, между A и B сначала должно существовать собственное отношение happens-before — например, через блокировку, volatile, Future.get, join или другой корректный механизм. Иначе B мог сам не увидеть запись A, а значит, задача не получит надёжно опубликованное состояние.
Нет. Гарантия публикации фиксирует действия до отправки задачи. Последующие записи могут произойти до фактического запуска задачи, но одного порядка во времени недостаточно: без отношения happens-before видимость не обеспечена.
Чтобы задача получила более позднее состояние, его нужно передать через отдельный синхронизированный канал или отправить задачу после завершения всех нужных записей. Особенно опасно передавать изменяемый объект и продолжать менять его после submit.
Нет. Он обеспечивает начальную публикацию действий, выполненных до отправки, но не превращает объект в неизменяемый и не защищает конкурентные изменения.
Если несколько потоков после запуска задачи меняют одни и те же поля, остаются обычные гонки данных: возможны потерянные обновления, несогласованные комбинации полей и отсутствие гарантии свежести. Для такой совместной работы нужны неизменяемость либо подходящий механизм синхронизации, например synchronized, Lock, volatile для подходящего одиночного состояния или атомарные классы.