反射和不变性应该如何协同工作



根据JSR-133,不可变对象是线程安全的,不需要同步。但是,可以使用反射来更新final字段的值:

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只是少数),我很好奇规范对这种情况说了什么。例如,如果我把字段更新放到同步块中,是否保证在其他线程中可见,或者值将缓存在寄存器中,按照final的规范。

http://download.oracle.com/otndocs/jcp/memory_model - 1.0 - pdf -规范oth - jspec/

如果对象的状态在构造后不能改变,则认为该对象不可变。http://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html

你正在使用可变对象,因为你正在改变它的状态。

使用反射确实打破了教程中定义的不变性,因为您可以使用它来更改非常量final字段。

一个抗反射不可变对象的例子如下:

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。但是,嘿,我们总是可以使用本机代码或内存编辑器将该值更改为其他值。

如果你坚持关闭访问控制并做一些调皮的事情,那么所有的下注都将被反射取消。

静态常量通常在编译时内联,所以改变它们的值可能不会有任何影响。结果实际上取决于编译时优化器有多聪明,以及JIT编译器在运行时有多聪明。

最终结果:"这里有龙,害怕所有敢踩在这里的人!"

在这种情况下发生内存一致性错误:

1线程1用b1.getField1()读取一个字段,得到1

2线程2用b1.setField1(2)

3现在当线程1调用b1.getField1()时,它可能会再次获得1,因为在没有同步的情况下,JVM允许优化该调用并返回缓存的值。

但是如果我们在实例化过程中只初始化一个可变bean一次(可能使用反射,像Spring容器一样),并且其他线程只在初始化后读取它,即使没有任何同步,也不会出现内存一致性错误

当你用反射改变不可变对象的字段时,同样的规则也适用于它们。

相关内容

  • 没有找到相关文章