2013-04-05 9 views
12

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

与えられた数(春とHibernateがほんの一部ですが)、このシナリオについて述べています。例えば。フィールド更新を同期ブロックにすると、他のスレッドでの可視性が保証されます。または、finalの仕様に従ってレジスタにキャッシュされます。

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

答えて

5

オブジェクトは、構成後に状態が変更できない場合、変更不可能と見なされます。 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残ります。しかし、ねえ、私たちはいつもネイティブコードやメモリエディタを使ってその値を他のものに変更することができます。

これを反射でハッキングするには、フィールドを最終的に呼び出すのではなく、操作方法を提供することもできます。

4

あなたがアクセス制御をオフにし、いたずらな事をすることを主張する場合は、すべてのベットがオフに反映しています。

通常、静的定数はコンパイル時にインライン展開されるため、値を変更しても影響はありません。その結果は、コンパイル時にオプティマイザがどれほど巧妙で、実行時にJITコンパイラがどれほど巧妙であるかによって大きく異なります。

最終結果:「ここではドラゴンズ、ここで踏みにじる人を恐れてください!」

3

メモリ一貫性エラーはこのシナリオで起こる:

1スレッド1 b1.getField1()を持つフィールドを読み取り、1つの

2スレッド2はb1.setField1とフィールドを変更します(2)

3スレッド1がb1.getField1()を呼び出すと、同期が存在しないため、JVMはこの呼び出しを最適化してキャッシュされた値を返すことができるため、再び1を得ることがあります。

しかし、(Springコンテナが行うよう、可能性の反射で)私たちは、インスタンス化時に一度だけ変更可能なBeanを初期化した場合と、他のスレッドが唯一の初期化後にそれを読んでます、でもどんなのsyncronization

なしなしメモリ一貫性エラーは存在しません同じルールは、フィールドを反映させて変更するときに不変オブジェクトに適用されます。

関連する問題