1

私は不変オブジェクトについては一種のダミーです。マルチスレッド環境で役立ち、定義上スレッドセーフであるという考えが浮かびました。

しかし、次のコードは逆のようです!

public class ImmutableTest {

    volatile ImmutableObject obj;

    public static void main(String[] args) throws InterruptedException {
        new ImmutableTest().execute();
    }

    private void execute() throws InterruptedException
    {
        obj = new ImmutableObject(0);
        ExecutorService exec = Executors.newFixedThreadPool(50);    
        for(int i = 0; i < 50; i++){
            exec.execute(new ImmutableRunnable(this));
        }
        Thread.sleep(5000);
        exec.shutdown();
        obj.print();
    }
}


public class ImmutableRunnable implements Runnable {
    ImmutableTest test;

    ImmutableRunnable(ImmutableTest immutableTest) {
        this.test = immutableTest;
    }

    public void run() {
        this.test.obj = new ImmutableObject(this.test.obj.getValue());
}

}

public final class ImmutableObject {

    private int n;

    public ImmutableObject(int newValue) {
        this.n = newValue;
        if(Math.random() > .5){
            try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
                e.printStackTrace();
        }
        }
    }

    public void print() {
        System.out.println(n);
    }

    public int getValue() {
        return n;
    }
}

スレッド セーフ環境では、結果として 50 が期待されます。しかし、そうではありません。ImmutableObject コンストラクターからランダム スリープを取り除くと、50 になります。

では、結論は?不変オブジェクトは「コンストラクターが十分に速い場合」スレッドセーフですか? または(おそらく)私は何かを誤解しましたか?

申し訳ありませんが、私の疑問を適切に説明していませんでした。これは同期に関する質問ではありません。メソッドを同期してロックを使用する方法を学びました。この質問は、不変オブジェクトと、それらのマルチスレッドとの関係に関するものです。不変オブジェクトはスレッドセーフであるため、恐れることなくスレッド間で共有できることをどこでも読みました。しかし、私見はまったく真実ではありません!!! 不変オブジェクトが異なるスレッド間で共有され、その使用を同期する必要がない例を教えてください。みんなありがとう。

4

4 に答える 4

3

あなたは何かを誤解しています。

不変オブジェクトを使用すると、ロックを使用せずにオブジェクトを安全に共有し、メソッド/フィールドを読み取ることができます。

この場合、何らかの方法で同期を行うために変更可能な参照に依存していますが、そうではありません obj

不変性とは、単に変更できないオブジェクトです。状態を変更したら、必要なことが実際に発生していることを確認するために、同期を実行する必要があります。たとえば、「obj を読み取って obj を設定する」呼び出し全体が排他的である必要があり、シリアルに発生します。

于 2012-09-28T01:38:14.497 に答える
3

ImmutableRunnable.run() はスレッドセーフではありません:

  • obj の値を読み取ります
  • 新しい ImmutableObject を作成します
  • objを更新します

これらの手順が同時に実行されている場合は、明らかに問題が発生する可能性があります。問題は不変オブジェクトではなく、適切なロックなしで obj を変更しているという事実です。

これを解決する最も簡単な方法は、ImmutableTest で同期することです。

于 2012-09-28T01:41:25.537 に答える
1
  1. 指定したコードは常に0を出力します。これは、値をコピーして変更しないため、開始値である0以外の値にすることはできません。
  2. ImmutableObjectは不変ではありません...不変オブジェクトにはfinalフィールドしかありません。これは、状態の不完全な公開を保証するものです。ただし、ここで実行しているコードにはまったく違いはありません。
  3. ImmutableObjectまたはImmutableRunnableのいずれかで値をインクリメントすることでコードを意図したとおりに修正すると、上記の問題が発生します。つまり、volatileは可視性/順序付けを保証しますが、アトミック性は保証しません。同期なしでコードを正しく機能させるには、AtomicReference.compareAndSetを使用して、置き換えられるImmutableObjectが次のオブジェクトのベースとして使用したものであることを確認します。例えば:

    AtomicReference obj = new AtomicReference();// was volatile obj
... change obj access in execute to match
... replace run method with following:
    ImmutableObject curr;
    do{
       curr = this.test.obj.get();
    }while(!this.test.obj.compareAndSet(curr, new ImmutableObject(curr.getValue()+1)));

于 2013-01-06T21:28:17.957 に答える
1

Yann Ramin が言ったように、不変オブジェクトを使用すると、ロックを使用せずにオブジェクトを安全に共有および使用できます。

ただし、ここでテストしているのは、 classのフィールドへの書き込みのスレッドセーフです。不変ではないため、スレッドセーフではありません。objImmutabletestImmutableTest

于 2012-09-28T10:19:04.067 に答える