0

Javaでの複数のスレッドの同期をより完全に理解しようとしています。私は、synchronizedキーワードの使用の背後にある高レベルのアイデアと、それがスレッド間の相互排除をどのように提供するかを理解しています。

唯一のことは、私がオンラインで読んだ教科書の例のほとんどは、同期されたキーワードを削除しても正しく機能することです。これにより、このトピックが必要以上に混乱します。

同期キーワードを含めないと誤った結果が生じる具体的な例を誰かに教えてもらえますか?どんな情報でも大歓迎です。

4

2 に答える 2

1

競合状態については、適切な同期を行わないと必ずしも発生しないということです。実際、かなり頻繁に正常に機能しますが、1年後の深夜にコードが発生します。バグはランダムにしか発生しないため、再現できない完全に予測不可能なバグでクラッシュします。

競合状態は、プログラムを常にクラッシュさせるとは限らず、多かれ少なかれランダムにトリガーされるため、非常に陰湿です。

于 2012-10-22T23:24:24.240 に答える
1

通常、反復回数を増やすことで競合状態をトリガーできます。これは、100回と1,000回の反復で機能するが、10,000回の反復(場合によっては)で失敗する(少なくとも私のクアッドコアボックスでは)簡単な例です。

public class Race
{
    static final int ITERATIONS = 10000;
    static int counter;

    public static void main(String[] args) throws InterruptedException {
        System.out.println("start");
        Thread first = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < ITERATIONS; i++) {
                    counter++;
                }
            }
        });
        Thread second = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < ITERATIONS; i++) {
                    counter++;
                }
            }
        });
        first.start();
        second.start();
        first.join();
        second.join();
        System.out.println("Counter " + counter + " should be " + (2 * ITERATIONS));
    }
}

>>> Counter 12325 should be 20000

counterアクセスが適切に同期されていないため、この例は失敗します。2つの方法で失敗する可能性があり、同じ実行で両方が失敗する可能性があります。

  • 一方のスレッドは、新しい値を認識しないため、もう一方のスレッドがカウンターをインクリメントしたことを認識できません。
  • 一方のスレッドは、現在の値を読み取ってから新しい値を書き込むもう一方のスレッド間のカウンターをインクリメントします。これは、インクリメント演算子とデクリメント演算子がアトミックではないためです。

この単純なプログラムの修正は、を使用することAtomicIntegerです。インクリメントの問題があるため、使用volatileするだけでは不十分ですがAtomicInteger、インクリメント、取得および設定などのアトミック操作を提供します。

于 2012-10-22T23:35:29.433 に答える