Java: сделать все поля окончательными или изменчивыми? - PullRequest
0 голосов
/ 12 декабря 2018

Если у меня есть объект, который разделяется между потоками, мне кажется, что каждое поле должно быть либо final, либо volatile со следующими причинами:

  • ifполе должно быть изменено (указать на другой объект, обновить значение примитива), тогда поле должно быть volatile, чтобы все остальные потоки работали с новым значением.Просто синхронизации методов, которые обращаются к указанному полю, недостаточно, потому что они могут возвращать кэшированное значение.

  • , если поле никогда не должно изменяться, сделайте его final.

Однако я не смог ничего найти по этому поводу, поэтому мне интересно, является ли эта логика ошибочной или просто слишком очевидной?

РЕДАКТИРОВАТЬ Конечно, вместо изменчивой можноиспользуйте final AtomicReference или аналогичный.

EDIT , например, см. Является ли метод-получатель альтернативой volatile в Java?

РЕДАКТИРОВАТЬ , чтобы избежать путаницы: Этот вопрос касается аннулирования кэша! Если два потока работают с одним и тем же объектом, поля объектов могут быть кэшированы (для каждого потока), если они не объявлены как энергозависимые.Как я могу гарантировать, что кэш недействителен должным образом?

ЗАКЛЮЧИТЕЛЬНОЕ РЕДАКТИРОВАНИЕ Спасибо @Peter Lawrey, который указал мне на JLS §17 (модель памяти Java).Насколько я вижу, он утверждает, что синхронизация устанавливает отношение между операциями, которое происходит до того, как поток увидит обновления из другого потока, если эти обновления произошли до того, например, если получатель и установщик для энергонезависимого поляsynchronized.

Ответы [ 3 ]

0 голосов
/ 12 декабря 2018

Хотя я чувствую, что private final, вероятно, должно было бы быть значением по умолчанию для полей и переменных с ключевым словом, таким как var, что делает его изменчивым, с использованием volatile, когда вам это не нужно,

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

если поле должно быть изменено (указать на другой объект, обновить значение примитива), то поле должно быть изменчивым, чтобы все остальные потоки работали с новым значением.

Хотя это хорошо для чтения, рассмотрим этот тривиальный случай.

volatile int x;

x++;

Это не поточно-ориентированный.Так же как и

int x2 = x;
x2 = x2 + 1; // multiple threads could be executing on the same value at this point.
x = x2;

Хуже всего то, что использование volatile затруднит поиск такого рода ошибки.

С точки зрения yshavit, обновление нескольких полей сложнееvolatile например HashMap.put(a, b) обновляет несколько ссылок.

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

synchronized дает вам все гарантии памяти volatile и более, поэтому он значительно медленнее.

ПРИМЕЧАНИЕ. Просто synchronized - для каждого метода тоже не всегда достаточно.StringBuffer синхронизирует каждый метод, но он хуже, чем бесполезный в многопоточном контексте, так как его использование может быть подвержено ошибкам.

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

С точки зрения синхронизации против изменчивости, этосостояния

Другие механизмы, такие как чтение и запись энергозависимых переменных и использование классов в пакете java.util.concurrent, предоставляют альтернативные способы синхронизации.

https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html

0 голосов
/ 12 декабря 2018

Если объект используется несколькими потоками, у вас есть две опции:

1.Сделайте этот объект доступным только для чтения

Таким образом, обновления (или кэш) не имеют никакого влияния.

2.Синхронизация на самом объекте

Кэширование недействительно сложно. Очень сложно. Поэтому, если вам нужно гарантировать отсутствие устаревших значений, вы должны защитить это значение и защитить блокировку вокруг указанного значения.

Сделать блокировку и значения какprivate для общего объекта, поэтому операции здесь являются деталями реализации.

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

0 голосов
/ 12 декабря 2018

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

С точки зрения создания других полей volatile:

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

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

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

Существует, по крайней мере, пара случаев, когда вам все равно необходимо использовать синхронизацию:

  • Вы можете использовать синхронизацию, если вам нужно было прочитать, а затем обновить одно или несколько полей атомарно.
    • Вы можете избежать синхронизации для определенных обновлений одного поля, например, если вы можете использовать класс Atomic* вместо «простого старого поля»;но даже для обновления одного поля вам все равно может потребоваться монопольный доступ (например, добавление одного элемента в список при удалении другого).
  • Кроме того, volatile / final может быть недостаточно дляпотокобезопасные значения, такие как ArrayList или массив.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...