Является ли это технически потокобезопасным, несмотря на то, что оно изменчиво? - PullRequest
8 голосов
/ 05 мая 2010

Да, закрытая переменная-член bar должна быть final верно? Но на самом деле, в данном случае, это атомарная операция - просто прочитать значение int. Так что это технически потокобезопасно?

class Foo {
    private int bar;
    public Foo(int bar) {
        this.bar = bar;
    }
    public int getBar() {
        return bar;
    }
}

// предполагаем, что бесконечное количество потоков повторно вызывает getBar в одном и том же экземпляре Foo.

EDIT:

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

1 Ответ

7 голосов
/ 05 мая 2010

Окончательное обновление: так что мой первый вывод оказался верным, просто мои рассуждения были ошибочными :-( Я отредактировал свой ответ, чтобы сделать его несколько связным, чтобы не скрывать следы моей предыдущей ошибки .

Заключение

Как указывал @Wyzard, даже если нет способа изменить bar после создания, Foo все еще не безопасен для потоков. Проблема не в атомарности, а в видимости. Если поток 1 изменяет значение bar в конструкторе (с его значения по умолчанию 0), нет никакой гарантии, когда другие потоки увидят новое значение (или увидят его вообще * 1011). *).

Итак, foo выглядит как неизменный объект. Цитата из Java-параллелизма на практике , раздел 3.4:

Объект неизменен, если:

  • Его состояние не может быть изменено после создания;
  • Все его поля являются окончательными; и
  • Он правильно построен (эта ссылка не скрывается во время строительства).

Foo выглядит нормально на 1) и 3), но не на 2). И это важный момент, из-за рассуждений выше. Объявление переменной final является одним из способов обеспечения ее видимости между различными потоками. Другие средства объявляют bar volatile или синхронизируют методы доступа. Но, конечно, в случае неизменного объекта, ни один из них не имеет большого смысла.

Конечные поля

Так почему final поля гарантируют видимость? Ответ от Java Concurrency на практике, раздел 3.5.2:

Поскольку неизменяемые объекты так важны, модель JavaMemory предлагает специальную гарантию безопасности инициализации для совместного использования неизменяемых объектов. Как мы уже видели, ссылка на объект становится видимой для другого потока, но это не обязательно означает, что состояние этого объекта является видимым для потока-потребителя. Чтобы гарантировать согласованное представление о состоянии объекта, необходима синхронизация.

С другой стороны, к неизменным объектам можно безопасно обращаться , даже если синхронизация не используется для публикации ссылки на объект . Для сохранения этой гарантии безопасности инициализации должны быть выполнены все требования неизменности: неизменяемое состояние, все поля являются окончательными и правильная конструкция. [...]

Неизменяемые объекты могут безопасно использоваться любым потоком без дополнительной синхронизации, даже если синхронизация не используется для их публикации.

Эта гарантия распространяется на значения всех конечных полей правильно построенных объектов; последние поля могут быть безопасно доступны без дополнительной синхронизации. Однако, если последние поля ссылаются на изменяемые объекты, синхронизация по-прежнему необходима для доступа к состоянию объектов, к которым они относятся.

А что будет, если поле будет не final? Другие потоки могут молча видеть устаревшее значение поля. Нет никаких исключений или каких-либо предупреждений - это одна из причин, почему такие ошибки очень трудно отследить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...