Параллелизм в Java - почему синхронизация установщика (но не получателя) не делает класс потокобезопасным?

Возможный дубликат:
Безопасность потоков в классе Java

Я читаю Параллелизм в Java на практике и наткнулся на пример, который меня озадачивает.

Авторы заявляют, что этот класс не является потокобезопасным.

public class MutableInteger {

    private int number;

    public int getInt() {
        return number;
    }

    public void setInt(int val) {
        number = val;
    }
}

И они также заявляют, что синхронизация только одного метода (например, установщика) не подойдет; вы должны синхронизировать оба.

У меня вопрос: почему? Разве не подошла бы синхронизация установщика?


person Pablo Fernandez    schedule 01.09.2010    source источник
comment
На самом деле тонкости разные.   -  person Michael Barker    schedule 01.09.2010


Ответы (4)


В Java есть случай до / после модели памяти. Для запуска этого поведения должна быть какая-то общая параллельная конструкция (например, синхронизированный блок / метод, блокировка, изменчивый, атомарный) как на пути записи, так и на пути чтения.

Если вы синхронизируете оба метода, вы создаете блокировку для всего объекта, которая будет совместно использоваться потоками чтения и записи. JVM гарантирует, что любые изменения, происходящие в потоке записи, которые происходят до выхода из (синхронизированного) метода setInt, будут видимы для любых потоков чтения после того, как они войдут в (синхронизированный) метод getInt. JVM вставит необходимые барьеры памяти, чтобы гарантировать, что это произойдет.

Если синхронизируется только метод записи, то изменения объекта могут быть не видны ни одному потоку чтения. Это связано с тем, что на пути чтения нет точки, которую JVM может использовать, чтобы гарантировать, что видимая память потока чтения (кеш и т. Д.) Соответствует потоку записи. Это обеспечит синхронизация метода getInt.

Примечание. В частности, в этом случае изменение значения поля 'number' в качестве изменчивого даст правильное поведение, поскольку чтение / запись в режиме изменяемого состояния также обеспечивает такое же поведение видимости памяти в JVM, а действие внутри метода setInt является только присваиванием.

person Michael Barker    schedule 01.09.2010
comment
Это типичная строка комментария. - person Andreas Dolk; 01.09.2010
comment
Изменение значения поля number не делает операцию присваивания атомарной. Метод должен быть синхронизирован - person John; 22.02.2011
comment
Объясните реальный сценарий отказа, если поле нестабильно, а метод не синхронизирован. - person Michael Barker; 23.02.2011

Это объясняется в книге перед образцом (стр. 35):

«Синхронизации только установщика будет недостаточно: потоки, вызывающие get, все равно смогут видеть устаревшие значения».

Устаревшие данные: когда поток чтения проверяет готовность, он может увидеть устаревшее значение. Если синхронизация не используется каждый раз при доступе к переменной, можно увидеть устаревшее значение для этой переменной. Хуже того, устаревание - это не все или ничего: поток может видеть обновленное значение одной переменной, но устаревшее значение другой переменной, которая была записана первой.

person Soundlink    schedule 01.09.2010
comment
Я читаю книгу, говоря, что она сможет увидеть устаревшие значения, недостаточно, мне нужны основы. - person Pablo Fernandez; 01.09.2010
comment
Итак, вы не верите, что есть возможность увидеть устаревшее значение, если получатель не синхронизирован, и вы хотите доказать это, или что вы имеете в виду? - person Soundlink; 02.09.2010
comment
Я хочу понять почему. Причина в том, что потоки хранят локальные кэшированные значения переменных, и чтение может не очищать кеш. Если бы кеша не было, не имело бы значения, были ли синхронизированные методы или нет (обе операции атомарны) - person Pablo Fernandez; 02.09.2010

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

person Wei S    schedule 01.09.2010

поскольку number не является изменчивым, а getInt() не синхронизирован, getInt() может возвращать устаревшие значения. Для получения дополнительной информации прочтите о модели памяти Java.

person Bwmat    schedule 01.09.2010
comment
Извините, но ответ по крайней мере неполный, а намек бесполезен для моих глаз. Он попросил объяснений по конкретной проблеме (-1) - person Andreas Dolk; 01.09.2010