14

According to JSR-133 immutable objects are thread safe and don't need synchronization. However it's possible to update values of final fields using reflection:

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());
    }
}

Given number of frameworks (Spring and Hibernate are only a few) which rely on reflection I'm curious what does spec says about this scenario. E.g. if I put field update into synchronized block will that guarantee visibility in other threads, or value will be cached in registers as per spec for final.

http://download.oracle.com/otndocs/jcp/memory_model-1.0-pfd-spec-oth-JSpec/

Petro Semeniuk
  • 6,970
  • 10
  • 42
  • 65

3 Answers3

6

An object is considered immutable if its state cannot change after it is constructed. http://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html

You are using that Object as mutable since you are changing its state.

It is true that the use of Reflection breaks immutability as defined in the tutorial, since you can use it to change non-constant final fields.

An example of a Reflection-resistant immutable object would be the following:

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());
}

In this instance your reflection test will fail, and the value will remain Integer.MIN_VALUE. But hey, we could always use native code or a memory editor to change that value to something else.

If you go this far into hacking with reflection, you might as well not call your field final and provide methods for manipulating it.

Ben Barkay
  • 5,473
  • 2
  • 20
  • 29
5

All bets are off with reflection if you insist on switching access control off and doing naughty things.

Static constants are usually inlined at compile time so changing their value probably would have no impact anyway. The result really depends on how clever the optimiser is when compiling, and how clever the JIT compiler is at run time.

End result: "Here be dragons, fear all who dare to tread here!"

Chris Cooper
  • 4,982
  • 1
  • 17
  • 27
3

Memory consistency errors happen in this scenario:

1 thread 1 reads a field with b1.getField1() and gets 1

2 thread 2 changes the field with b1.setField1(2)

3 now when thread 1 calls b1.getField1() it may get 1 again, because in absence of synchronization JVM is allowed to optimize this call and return a cached value.

But if we initialized a mutable bean only once during instantiation (possibly with reflection, like Spring container does) and other threads will only read it after initialization, there will be no memory consistency errors even without any syncronization

The same rules apply to immutable objects when you change their fields with reflection.

Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275