0

私は2つのスレッドでコードを試していました.1つのスレッドは共有された長い変数をインクリメントし、もう1つのスレッドは変数をデクリメントします.

class Shared {
    private long a;
    public void incr() {
        a++;
    }
    public void dec() { a--; }
    public long getA(){return a;}
}

この共有オブジェクトを 2 つのスレッドに渡しています。各スレッドでインクリメントまたはデクリメントする Nitems の回数

Shared obj = new Shared();
Incrementer incrementer = new Incrementer(obj, nitems);
Decrementer decrementer = new Decrementer(obj , nitems);

減分スレッドの実行方法:-

public void run()
{
    for(int i=0; i<nitems; ++i)
    {
        s.dec();
    }
}

インクリメント スレッドの実行方法:-

public void run()
{
    for(int i=0; i<nitems; ++i)
    {
        s.incr();

    }
}

私がそれを実行すると。私は問題をはっきりと見ることができました。コード全体を 20 回実行した後、結果はゼロではありません。このように変更すると、同じ実行方法になります

//increment
    public void run()
    {
        for(int i=0; i<nitems; ++i)
        {
            s.incr();
            System.out.println("ghijk");

        }
    }

//decrement
    public void run()
{
    for(int i=0; i<nitems; ++i)
    {
        s.dec();
        System.out.println("abcdef");
    }
}

値がゼロでなかった 1 ~ 2 回を除いて、結果はほぼ常にゼロです。私の質問は、この SOP が導入されたときに、このコードが適切に機能するようになった理由です??? incr() メソッドと dec() メソッドを同期した後でのみ、出力としてゼロが生成されると思いました。

4

3 に答える 3

1

他の人が指摘したように、incr()anddec()メソッドは なしではスレッドセーフではありませんsynchronize

を追加するSystem.out.printlnと、単にタイミングが変わるため、動作が変わる可能性があります。また、この呼び出しには、ersatz として機能する同期が埋め込まれていますsynchronize。これは、まったく同期しないよりもうまく機能します。考えられる一連のイベントは次のとおりです。

  1. Thread-1はカウンターを 1 増やします (最初に開始されるため、最初に実行されます)。
  2. スレッド 1は印刷に進みます。
  3. Thread-1が印刷でビジー状態である間、 Thread-2はカウンターをデクリメントします。
  4. Thread-2はデクリメントを完了します ( Thread-1 が印刷を終了する前に)。
  5. スレッド 2は印刷を開始しますが、スレッド 1の出力ストリームがロックされているため、待機する必要があります。
  6. スレッド 2が印刷中である間、スレッド 1はステップ 1 にループバックします。

したがって、印刷には同期ブロックがあるため、2 つのスレッドがどちらが変数を変更しているか、どちらが印刷しているかを交互に切り替えることができます。場合によっては、タイミングがずれたり、競合状態が発生したり、カウントが同期しなくなったりすることがあります。

マルチコア システムで発生する可能性があるもう 1 つの問題は、Java スレッドが変数値のローカル バージョンを維持できるため、各スレッドが独自のコピーを参照する可能性があることです。これを防ぐには、変数を宣言する必要がありますvolatile。不揮発性変数は、synchronizeブロックが検出されたときにスレッド間で同期されるため、volatileここでは必要ありません。( のもう 1 つの副作用は、が 2 つのスレッド間printlnで確実にa同期されるようにすることです。)

簡単にするためにAtomicLong、concurrency パッケージから使用します。

于 2013-03-28T12:44:15.630 に答える
1

複数のスレッドがフィールドにアクセスできる場合は、同期する必要があります。

于 2013-03-28T12:35:08.440 に答える
0

を変更するメソッドへのアクセスを同期しますa

class Shared {
    private long a;
    public synchronized void incr() {
        a++;
    }
    public synchronized void dec() {
        a--;
    }
    public long getA(){return a;}
}
于 2013-03-28T12:41:52.250 に答える