9

以下のコードスナイパーを検討してください。

package sync;

public class LockQuestion {
    private String mutable;

    public synchronized void setMutable(String mutable) {
        this.mutable = mutable;
    }

    public String getMutable() {
        return mutable;
    }   
}

Time1スレッドThread1は、「可変」変数を更新します。ローカルキャッシュからメインメモリにメモリをフラッシュするには、setterで同期が必要です。時間Time2(Time2> Time1、スレッド競合なし)で、スレッドThread2は可変の値を読み取ります。

質問は–ゲッターの前に同期させる必要がありますか?これによって問題が発生することはないようです。メモリは最新であり、Thread2のローカルキャッシュメモリはThread1によって無効化および更新される必要がありますが、よくわかりません。

4

6 に答える 6

4

不思議ではなく、java.util.concurrentのアトミック参照を使用しないのはなぜですか?

(そして、その価値については、私の読んだことは、 Thread2が同期を使用しない限り、可変への変更を確認することを保証するものではありません...しかし、私は常にJLSのその部分から頭痛がするので、アトミック参照を使用してください)

于 2010-12-01T22:34:12.307 に答える
4

可変の揮発性を作成すれば問題ありません。詳細は「安価な読み取り/書き込みロック」にあります。

于 2010-12-01T22:45:12.647 に答える
1

セッターが呼び出された後にのみゲッターが呼び出されることを絶対に確信していますか?その場合、同時読み取りを同期する必要がないため、ゲッターを同期する必要はありません。

getとsetを同時に呼び出すことができる可能性がある場合は、必ず2つを同期する必要があります。

于 2010-12-01T22:34:20.463 に答える
1

読み取りスレッドのパフォーマンスについて非常に心配している場合は、適切な同期または揮発性またはアトミック参照を使用して、値を1回読み取るだけです。次に、値を単純な古い変数に割り当てます。

プレーン変数への割り当ては、アトミック読み取り後に発生することが保証されており(他にどのようにして値を取得できるため)、値が別のスレッドによって再度書き込まれることがない場合は、すべて設定されています。

于 2010-12-01T23:15:18.903 に答える
1

正しいものから始めて、後で問題があることがわかったら最適化する必要があると思います。数ナノ秒が長すぎない限り、AtomicReferenceを使用します。;)

public static void main(String... args) {
    AtomicReference<String> ars = new AtomicReference<String>();
    ars.set("hello");
    long start = System.nanoTime();
    int runs = 1000* 1000 * 1000;
    int length = test(ars, runs);
    long time = System.nanoTime() - start;
    System.out.printf("get() costs " + 1000*time / runs + " ps.");
}

private static int test(AtomicReference<String> ars, int runs) {
    int len = 0;
    for (int i = 0; i < runs; i++)
        len = ars.get().length();
    return len;
}

プリント

get() costs 1219 ps.

psはピコ秒で、100万分の1マイクロ秒です。

于 2010-12-01T23:44:49.837 に答える
0

これによって誤った動作が発生することはおそらくありませんが、スレッドが起動する順序も保証しない限り、コンパイラがThread1での書き込みの前にThread2での読み取りを並べ替えなかったことを必ずしも保証することはできません。より具体的には、Javaランタイム全体は、スレッドがシリアルで実行されているかのように実行されることを保証するだけで済みます。したがって、スレッドが最適化の下でシリアルに実行されている同じ出力を持っている限り、言語スタック全体(コンパイラー、ハードウェア、言語ランタイム)はほとんど何でも実行できます。Thread2がの結果をキャッシュできるようにすることを含みLockQuestion.getMutable()ます。

実際には、それが起こったとしたら、私は非常に驚きます。これが起こらないことを保証したい場合は、LockQuestion.mutableとして宣言さfinalれ、コンストラクターで初期化されます。または、次のイディオムを使用します

private static class LazySomethingHolder {
  public static Something something = new Something();
}

public static Something getInstance() {
  return LazySomethingHolder.something;
}
于 2010-12-01T23:09:10.073 に答える