7

は、以下のコードの狙撃を検討発生しないことができれば、読みながら、必要に応じて同期されています。メモリをローカルキャッシュからメインメモリにフラッシュするためには、セッタで同期が必要です。 時刻Time2(Time2> Time1、スレッド競合なし)スレッドThread2はmutableの値を読み取ります。には競合が

質問です - 私はゲッターの前に同期を入れる必要がありますか?このように見えるのは問題ありません。メモリは最新で、Thread2のローカルキャッシュメモリは無効にする必要があります。&がThread1によって更新されましたが、わかりません。

+6

Javaでのスレッド化は、* happen-before * relationshipsの観点から定義されています。あなたが間違っているので、キャッシュをフラッシュするという観点から考えるべきではありません。一つは、コンパイラの最適化が重要です。フラッシングキャッシュが正確なモデルであったとしても、参照されるオブジェクトが変更可能であれば、更新の順序は保証されません。 (1.5仕様までは、仕様はフラッシングキャッシュモデルを使用していましたが、実装できませんでした) –

答えて

4

不思議ではなく、単に原子の参照をjava.util.concurrentで使用しないでください。

の私の読書は、スレッド2が同期化を使用しない限り、スレッド2が変更可能であることを保証するものではありません...しかし、私は常にJLSのその部分から頭痛を得るので、参照)

+0

Atomicクラスは非常に便利ですが、必然的に小さなパフォーマンス上のペナルティがあります。私は、アップデートのアトミック性が必要でない限り、「シンプルな」タイプを使用することを好みます。上記の場合は、更新の表示のみが必要です。 –

+0

@Petro Semeniuk原子的オブジェクト、同期、および/または揮発性のいずれかがなければ、更新の「可視性」とお互いの関係での読み取りを保証する方法はありません。これは重要かもしれません。 –

+0

'synchronized'と' volatile 'は同じオブジェクト上にある必要があることに注意してください。 –

1

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

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

+1

これは当てはまりません。ゲッターに同期化されたロックがなければ、万一起こることはありません。 setterが起動して終了し、getterが別のスレッドで起動しますが、*は*古い値を読み込みます。これは重要かもしれません。シングルアクセスの場合、「volatile」はここでは「同等」となりますが、より大きな同期の影響を逃してしまいます。 –

+0

OPは、Time1でsetterが呼び出され、Time2でgetterが呼び出され、Time2> Time1であると書いているので、スレッド1が完了した後にのみスレッド2が開始される可能性があります。 – SpeksETC

+2

スレッドが停止または結合されていることに関して何も表示されません。 T2> T1はhappen-before-cachingには不十分です。この仮定をウィンドウの外に投げます。 –

4

あなたは、揮発性の可変詳細を作る場合、それは大丈夫だろう「cheap read-write lock

+0

ありがとう。揮発性についての私の考えは、Atomic *クラスと同様です - 安価ですが、無料ではありません。理想的には、私は強制的に '起こす前に'、しかし、ペナルティなしで/読み込みをロックしたい。 –

+0

@Petro Semeniukこれは、「いつも望ましいセマンティクスを持っている」ということです(例えば、奇妙な同期バグを入力させないなど)。しかし、*あなたのプログラム*は、 "高価なロック"(これは、非常に争われていない限り、非常に安価です)のような方法でうまく機能するかもしれません - しかし、それはあなたが大きな画像で分析するためだけです。 「正しいこと」を行うだけでなく、愚かなマイクロ最適化について心配しないでください。 –

+0

実際には、「揮発性」はほとんど役に立ちませんが。 –

0

これはおそらく、不正な動作が発生することはありませんが、あなたはまた、スレッドがで起動順序を保証しない限り、あなたは必ずしも保証をすることはできませんスレッド1での書き込みの前に、コンパイラがThread2の読み込み順序を変更しなかったことを示します。より具体的には、the entire Java runtime only has to guarantee that threads execute as if they were run in serial。だから、スレッドが最適化の下で連続して実行されている同じ出力を持つ限り、言語スタック全体(コンパイラ、ハードウェア、言語ランタイム)は、何でも望みどおりに を実行できます。スレッド2がLockQuestion.getMutable()の結果をキャッシュすることを許可することを含む。

実際に起こったことがあれば、私は非常に驚くでしょう。これが起こらないようにするには、LockQuestion.mutablefinalと宣言し、コンストラクタで初期化します。 Or use the following idiom

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

public static Something getInstance() { 
    return LazySomethingHolder.something; 
} 
1

あなたが読書スレッドでのパフォーマンスについてそんなに心配なら、あなたは適切な同期または揮発性または原子の参照を使用して値を1回読まれる何。次に、値を普通の古い変数に代入します。

プレーン変数への代入は、アトミックな読み取りの後に起こることが保証されています(他にどのように値を取得できますか?)。また、値が決して別のスレッドによって再度書き込まれることはありません。

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は、ピコ秒です。

+0

perf metricsを提供してくれてありがとう。 Btw、もしあなたがプレーンストリングで同じことをするなら、AtomicReferenceへの絶え間ないアクセスが検索コストを2倍にすることがわかります。おそらく最も効率的な方法はローカル変数に値を代入することです(テストメソッド内) –

+0

getは揮発性の読み込みで、volatileと同じ効果があると宣言します。 –