Почему переменная-член объекта не может быть как окончательной, так и изменчивой в Java?
Если в классе у меня есть экземпляр ConcurrentHashMap, который будет изменен и прочитан несколькими потоками, я могу определить так:
public class My Class {
private volatile ConcurrentHashMap<String,String> myMap = new ConcurrentHashMap<String,String>();
...
}
добавление final
в поле myMap приводит к ошибке, говорящей, что я могу использовать только final или volatile. Почему это может быть не только?
Ответы
Ответ 1
volatile
имеет значение только для модификаций самой переменной, а не для объекта, к которому она относится. Нет смысла иметь поле final volatile
, потому что окончательные поля не могут быть изменены. Просто объявите поле final
, и все должно быть хорошо.
Ответ 2
Это из-за модели памяти Java (JMM).
По сути, когда вы объявляете поле объекта как final
, вам нужно инициализировать его в конструкторе объекта, а затем поле final
не изменит его значение. И JMM promises, что после завершения ctor любой поток увидит то же (правильное) значение поля final
. Таким образом, вам не нужно использовать явную синхронизацию, такую как synchronize
или Lock
, чтобы разрешить всем потокам видеть правильное значение поля final
.
Когда вы объявляете поле объекта как volatile
, значение поля может меняться, но при каждом чтении значения из любого потока будет отображаться последнее значение, записанное в него.
Таким образом, final
и volatile
достигают той же цели - видимость значения поля объекта, но сначала специально используется для переменной, которая может быть назначена только один раз и второй для переменной, которая может быть изменена многократно.
Ссылки:
Ответ 3
Потому что volatile
и final
являются двумя крайними концами в Java
volatile
означает, что переменная привязана к изменениям
final
означает, что значение переменной никогда не изменится вообще
Ответ 4
Поле A volatile
дает вам гарантии того, что происходит при его изменении. (Нет объекта, на который он мог бы ссылаться)
Поле A final
не может быть изменено (что может быть изменено ссылкой на поля)
Нет смысла иметь оба.
Ответ 5
volatile
используется для переменных, которые могут изменяться в некоторых случаях, в противном случае нет необходимости в volatile
, а final
означает, что переменная может не измениться, поэтому нет необходимости в volatile
.
Ваши проблемы concurrency важны, но если HashMap
volatile не решит проблему, для обработки проблем concurrency вы уже используете ConcurrentHashMap
.
Ответ 6
Потому что это не имеет никакого смысла. Volatile влияет на ссылочное значение объекта, а не на поля объекта и т.д.
В вашей ситуации (у вас есть параллельная карта) вы должны сделать поле final
.
Ответ 7
Модификатор volatile
гарантирует, что все чтения и записи переходят в основную память, т.е. как доступ к переменной почти в блок synchronized
. Это не имеет значения для конечной переменной, которая не может быть изменена.