Как размышление и неизменность должны работать вместе
Согласно JSR-133 неизменяемые объекты являются потокобезопасными и не нуждаются в синхронизации. Однако возможно обновить значения конечных полей с помощью отражения:
package com.stackoverflow;
import java.lang.reflect.Field;
public class WhatsGoingOn {
static class Immutable {
private final int value;
public Immutable(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
final Immutable immutable = new Immutable(Integer.MIN_VALUE);
final Field f = Immutable.class.getDeclaredField("value");
f.setAccessible(true);
System.out.println(immutable.getValue());
f.set(immutable, Integer.MAX_VALUE);
System.out.println(immutable.getValue());
}
}
Учитывая количество фреймворков (Spring и Hibernate - всего лишь несколько), которые полагаются на размышления, мне любопытно, что спецификация говорит об этом сценарии. Например. если я поместил обновление поля в синхронизированный блок, который обеспечит видимость в других потоках, или значение будет кэшироваться в регистрах по спецификации для окончательного.
http://download.oracle.com/otndocs/jcp/memory_model-1.0-pfd-spec-oth-JSpec/
Ответы
Ответ 1
Объект считается неизменным, если его состояние не может измениться после его построения. http://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html
Вы используете этот объект как изменяемый, так как вы меняете его состояние.
Верно, что использование Reflection нарушает неизменность, как определено в учебнике, так как вы можете использовать его для изменения непостоянных конечных полей.
Примером устойчивого к отражению неизменяемого объекта будет следующее:
static class Immutable {
// This field is a constant, and cannot be changed using Reflection
private final int value = Integer.MIN_VALUE;
public int getValue() {
return value;
}
}
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
final Immutable immutable = new Immutable();
final Field f = Immutable.class.getDeclaredField("value");
f.setAccessible(true);
System.out.println(immutable.getValue());
f.set(immutable, Integer.MAX_VALUE);
System.out.println(immutable.getValue());
}
В этом случае ваш тест на отражение завершится неудачно, и значение останется Integer.MIN_VALUE
. Но эй, мы всегда могли использовать собственный код или редактор памяти, чтобы изменить это значение на что-то еще.
Если вы заходите так далеко от взлома с отражением, вы также можете не назвать свое поле окончательным и предоставить методы для его управления.
Ответ 2
Все ставки отключены с отражением, если вы настаиваете на отключении контроля доступа и выполнении непослушных вещей.
Статические константы обычно устанавливаются во время компиляции, поэтому изменение их значения, вероятно, не будет иметь никакого влияния. Результат действительно зависит от того, насколько умный оптимизатор при компиляции и насколько умный компилятор JIT находится во время выполнения.
Конечный результат: "Вот драконы, бойтесь всех, кто осмелится ступить сюда!"
Ответ 3
Ошибки согласованности памяти происходят в этом сценарии:
1 thread 1 читает поле с b1.getField1() и получает 1
2 thread 2 изменяет поле с помощью b1.setField1 (2)
3 теперь, когда поток 1 вызывает b1.getField1(), он может снова получить 1, потому что в отсутствие синхронизации JVM разрешено оптимизировать этот вызов и возвращать кешированное значение.
Но если мы инициализировали mutable bean только один раз во время создания экземпляра (возможно, с отражением, например, с контейнером Spring), а другие потоки будут читать его только после инициализации, ошибки консистенции памяти не будут даже без какой-либо синхронизации
Те же правила применяются к неизменяемым объектам при изменении их полей с отражением.