2017-03-25 6 views
4

マルチスレッドの概念を試すだけで、私は悲観的なロックを使用するAtomicIntegerの独自のバージョンを実装しています。プリミティブなインスタンス変数をvolatileにする必要がありますか?

public class ThreadSafeInt { 
    public int i; // Should this be volatile? 

    public ThreadSafeInt(int i) { 
     this.i = i; 
    } 

    public synchronized int get() { 
     return i; 
    } 

    public synchronized int getAndIncrement() { 
     return this.i++; 
    } 
    // other synchronized methods for incrementAndGet(), etc... 
} 

私は、ThreadSafeIntのインスタンスを取りテストを書いたスレッドの数百人にそれを与え、それらのスレッドのそれぞれが10万回getAndIncrementを呼び出します:それは次のようになります。私が見ているのは、整数の値が正確に(number of threads) * (number of increments per thread)であり、プリミティブなインスタンス変数iでvolatileを使用していないにもかかわらず、すべての増分が正しく行われることです。私は、私はiが揮発しなかった場合、私は、例えば、スレッド1ずつ1から0からiが、スレッド2はまだ0の値を見て、視認性の問題の多くを得るだろうし、また、唯一の1にそれをインクリメントすることを期待しました正しい値よりも小さい最終値が生じます。

私は私のテストは、可視性の問題のための固有の可能性があっても正常に動作するように見えることができるように、視認性の問題は、ランダムに発生し、私の環境の特性に依存することを理解しています。だから私は揮発性のキーワードがまだ必要であると思う傾向がある。

しかし、これは正しいでしょうか?または、私のコードのいくつかのプロパティがありますか(おそらく、それがちょうど原始的な変数であるという事実)、私は実際に揮発性キーワードの必要性を取り除くために信頼できるでしょうか?私は原始的なインスタンス変数に揮発使用していないにもかかわらず、

答えて

5

。私は、私が揮発しなかった場合は、その後、私はあなたのgetAndIncrement()get()方法​​にすることで、視認性の問題

の多くを得るだろうと予想iを変更しているすべてのスレッドが適切にアップデートの両方のためにそれをロックしていますその値の検索を行います。​​ブロックは、ivolatileであることを必要としないため、メモリ同期も保証されます。あなたがvolatile intフィールドをラップ代わりにAtomicIntegerを使用しなければならない、と述べた

AtomicIntegergetAndIncrement()方法はまだスレッドセーフでありながらもはるかに高速である​​ブロックに頼らずに値を更新します。

public final AtomicInteger i = new AtomicInteger(); 
... 
// no need for synchronized here 
public int get() { 
    return i.get(); 
} 
// nor here 
public int getAndIncrement() { 
    return i.getAndIncrement(); 
} 

私は、視認性の問題、例えば、スレッド1、iをインクリメントして0から1までの多くを得るだろうが、スレッド2はまだ0の値を見ても、最終的に引き起こして、1つだけにそれをインクリメント正しい値よりも小さい値を返します。

あなたget()方法は​​なかった場合は、あなたの増分は右に扱われるかもしれないが、他のスレッドがiの値が正しく発行され表示されません。しかし、両方の方法が​​の場合、読み取りと書き込みのメモリ同期が保証されます。​​もロックを行い、i++を実行できます。再びAtomicIntegerはメモリ同期とインクリメント競合状態をはるかに効率的に処理します。

より具体的には、​​ブロックが入力されると、volatileフィールドからの読み出しと同じ読み出しメモリバリアーを横切ります。​​ブロックが終了すると、書き込みメモリバリアを横切り、volatileフィールドへの書き込みと同じです。​​ブロックとの違いは、一人の人物が一度に特定のオブジェクトを確実にロックしていることを保証するためのロックもあることです。

+0

あなたは、コードパスが一度に1つのスレッドでしか実行できないことを保証するだけでなく、同期ブロック内で触れられた変数がvolatileであるかのように扱われることを保証しますか?私はあなたが同期を使用しても、2つのスレッドが変数内の異なる値を観測する可能性があると考えました。つまり、あなたが言っているのは、そうではないということです。 – russell

+0

また、私は 'LongAdder'に渡してもよいでしょう。 –

+0

@russellこれが本当に意味するのは、通常、アトミックな操作ではない 'i ++'は、 'synchronized'のためにアトミックになります。 –

関連する問題