Нужно ли синхронизировать доступ к неизменяемым типам в Java? - PullRequest
1 голос
/ 30 апреля 2011

Допустим, у меня есть этот класс:

class Zoo
{
    protected String bearName;
    protected Double trainerSalary;
    protected Integer monkeyCount;
}

Может ли один поток записывать в эти поля, а другой - читать их, не требуя synchronized доступа к объекту Zoo?

Примечание: эти значения могут обрабатываться отдельно друг от друга, поэтому не имеет значения, что trainerSalary изменяется при считывании monkeyCount.

EDIT :

Просто для пояснения, поля являются изменяемыми ;* их объекты, на которые есть ссылки, неизменны .

Ответы [ 5 ]

6 голосов
/ 30 апреля 2011

Технически вам нужно сделать их final, volatile или прочитать и записать их, используя synchronzied, чтобы гарантировать, что читатель будет читать самые последние значения.Как вы понимаете, если один поток записывает значение, нет гарантии, что другой поток прочитает то же значение.Это потому, что поток чтения может видеть кэшированное значение.Это более вероятно с многоядерными процессорами и различными уровнями кэша.

Отличная книга по этому вопросу - Параллелизм Java на практике .

4 голосов
/ 30 апреля 2011

Доступ и обновления в ячейках памяти, соответствующих полям любого типа, кроме long или double, гарантированно будут атомарными (см. Параллельное программирование в Java) .Вот почему можно ожидать, что вам не нужно синхронизировать доступ на чтение к вашим полям.Однако модель памяти Java позволяет потокам кэшировать ранее прочитанные значения в случае, если вы обращаетесь к ним повторно, поэтому вы должны пометить поля как энергозависимые, чтобы каждый поток видел самые последние значения.

Если вы уверены, что никто не изменит значения полей, сделайте их окончательными.В этом случае нет необходимости в изменчивом поле.

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

1 голос
/ 15 августа 2013

Мои 2 цента, из «Языка программирования Java», 4-е изд., 14.10.2: «Существует распространенное заблуждение, что общий доступ к неизменным объектам не требует какой-либо синхронизации, поскольку состояние объекта никогда не меняется.В общем, это неправильное представление, потому что оно основано на предположении, что потоку гарантированно будет видно инициализированное состояние неизменяемого объекта, и это не обязательно должно быть так. Проблема в том, что, хотя общий объект является неизменным, ссылка используетсядоступ к общему объекту сам по себе является общим и часто изменяемым - следовательно, правильно синхронизированная программа должна синхронизировать доступ к этой общей ссылке, но часто программы этого не делают, потому что программисты не осознают необходимость делать это.поток создает объект String и сохраняет ссылку на него в статическом поле. Затем второй поток использует эту ссылку для доступа к строке. На основании того, что мы обсудили до сих пор, нет гарантии, что значения записываютсяn первым потоком при построении строки будет виден второму потоку при доступе к строке. "

1 голос
/ 30 апреля 2011

Поскольку вы указали класс, другой класс в том же пакете может изменить эти значения.Этот класс не является неизменным.

Теперь, если бы вы сделали что-то вроде

class Zoo
{
    protected final String bearName;
    protected final Double trainerSalary;
    protected final Integer monkeyCount;
}

, тогда класс был бы неизменным.Если логика вашей программы рассматривает этот класс как неизменяемый, то почему бы не сделать его фактически неизменным?

Кроме того, если несколько потоков проверяют и обновляют одно и то же значение, это может вызвать проблемы.Скажем, несколько потоков проверяли и обновляли monkeyCount, тогда есть большая вероятность, что monkeyCount окажется неверным, потому что нет ничего, что заставляло бы эти проверки и обновления происходить атомарно.

0 голосов
/ 30 апреля 2011

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

protected Integer monkeysLeft;
protected Integer monkeysEatenByBears;

, где две переменные логически связаны, вам нужен синхронизированный доступ к паре из них.

...