Как вы сказали: " Каждое действие в потоке происходит перед каждым действием в этом потоке, которое происходит позже в порядке программы. "
Очевидно, что если поток мог каким-то образом получить доступ к объекту еще до того, как был вызван конструктор, вы бы облажались. Поэтому что-то должно препятствовать доступу к объекту до того, как его конструктор вернется. Но когда конструктор возвращается, все, что позволяет другому потоку обращаться к объекту, безопасно, потому что это происходит после в программном порядке конструирующего потока.
Базовая безопасность потоков с любым совместно используемым объектом достигается за счет того, что все, что позволяет потокам обращаться к объекту, не выполняется до тех пор, пока конструктор не вернется, установив, что все, что может сделать конструктор, произойдет до того, как любой другой поток сможет получить доступ к объекту.
Поток:
- Объект не существует и недоступен.
- Некоторые потоки вызывают конструктор объекта (или делают все остальное, что необходимо для подготовки объекта к использованию).
- Затем этот поток что-то делает, чтобы другие потоки могли получить доступ к объекту.
- Другие потоки теперь могут получить доступ к объекту.
Программный порядок потока, вызывающего конструктор, гарантирует, что никакая часть 4 не произойдет, пока не будут выполнены все 2.
Обратите внимание, что это относится точно так же, если что-то должно быть сделано после того, как конструктор вернется, вы можете просто считать их логически частью процесса построения. Точно так же части работы могут выполняться другими потоками, если все, что требуется для просмотра работы, выполненной другим потоком, не может начаться, пока не будет установлена какая-либо связь с работой, которую выполнял другой поток.
Разве это не на 100% отвечает на ваш вопрос?
Переформулировать:
Как это быть потокобезопасным? Что гарантирует взаимосвязь «происходит до» между записями, выполняемыми в Builder, и чтениями из RegularImmutableList?
Ответ заключается в том, что все, что препятствовало доступу к объекту до того, как был даже вызван конструктор (что должно быть чем-то, иначе мы были бы полностью испорчены), продолжает препятствовать доступу к объекту до тех пор, пока конструктор не вернется. Конструктор фактически является атомарной операцией, потому что никакой другой поток не может попытаться получить доступ к объекту во время его работы. После того, как конструктор вернется, все, что поток, вызвавший конструктор, чтобы позволить другим потокам получить доступ к объекту, обязательно произойдет после того, как конструктор вернется, потому что "[e] ach действие в потоке происходит перед каждым действием в этом потоке, которое происходит позже в порядке программы. "
И еще раз:
Если какой-то поток строит список и передает его ссылку на другие потоки без использования блокировок (например, через конечное или изменяемое поле), я не вижу, что гарантирует безопасность потока. Чего мне не хватает?
Поток сначала строит список, а затем передает его ссылку. Построение списка «происходит - перед каждым действием в этом потоке, которое происходит позже в порядке программы» и, таким образом, происходит - до передачи ссылки. Таким образом, любой поток, который видит передачу ссылки, происходит после завершения построения списка.
Если бы это было не так, не было бы хорошего способа построить объект в одном потоке, а затем дать другим потокам доступ к нему. Но это совершенно безопасно, потому что любой метод, который вы используете для передачи объекта из одного потока в другой, обязательно установит связь.