Как размышление и неизменность должны работать вместе

Согласно 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), а другие потоки будут читать его только после инициализации, ошибки консистенции памяти не будут даже без какой-либо синхронизации

Те же правила применяются к неизменяемым объектам при изменении их полей с отражением.