0

さびた Java を練習するために、単純なマルチスレッド共有データの例を試してみたかったのですが、驚くべきことに出会いました。

基本的にAtomicInteger、3 つのスレッド間に共有カウンターがあり、それぞれが順番にインクリメントしてカウンターを出力します。

主要

AtomicInteger counter = new AtomicInteger(0);

CounterThread ct1 = new CounterThread(counter, "A");
CounterThread ct2 = new CounterThread(counter, "B");
CounterThread ct3 = new CounterThread(counter, "C");

ct1.start();
ct2.start();
ct3.start();

カウンタースレッド

public class CounterThread extends Thread
{   
    private AtomicInteger _count;
    private String _id;

    public CounterThread(AtomicInteger count, String id)
    {
        _count = count;
        _id = id;
    }

    public void run()
    {
        while(_count.get() < 1000)
        {
            System.out.println(_id + ": " + _count.incrementAndGet());
            Thread.yield();
        }
    }
}

各スレッドが実行されるThread.yield()と、別のスレッドに実行を譲って次の_countようにインクリメントすることを期待していました。

A: 1
B: 2
C: 3
A: 4
...

代わりに、 100 回Aインクリメント_countしてから に渡す出力を取得しましたB。3 つのスレッドすべてが一貫して交代することもあれば、1 つのスレッドがいくつかのインクリメントを支配することもあります。

Thread.yield()処理を常に別のスレッドに譲らないのはなぜですか?

4

2 に答える 2

2

各スレッドが Thread.yield() を実行すると、別のスレッドに実行を渡して、次のように _count をインクリメントすることを期待していました。

回転しているスレッド化されたアプリケーションでは、出力を予測することは非常に困難です。A:1 B:2 C:3 ...完璧なタイプの出力を得るには、ロックなどで多くの作業を行う必要があります。

問題は、すべてが競合状態であり、ハードウェア、競合状態、タイム スライスのランダム性、およびその他の要因により予測できないことです。たとえば、最初のスレッドが開始されると、次のスレッドが開始されるまでに数ミリ実行される場合があります。する人はいないでしょうyield()。また、結果が得られたとしても、4 プロセッサのボックスを使用している可能性があるため、他のスレッドを一時停止する理由はまったくありません。

代わりに、A が _count を 100 回インクリメントしてから B に渡す出力を取得しました。3 つのスレッドすべてが一貫して順番に実行されることもあれば、1 つのスレッドが複数のインクリメントを支配することもありました。

そうですね、通常、このスピン ループでは、タイム スライスを取得するときに、単一のスレッドからの出力のバーストが見られます。これは、タイミングにも影響を与えるSystem.out.println(...)という事実によっても混乱します。synchronized同期操作を行っていない場合は、さらにバースト性の高い出力が表示されます。

Thread.yield() が常に別のスレッドに処理を譲らないのはなぜですか?

私はめったに使用しませんThread.yield()。これはせいぜいスケジューラへのヒントであり、一部のアーキテクチャではおそらく無視されます。スレッドを「一時停止」するという考えは、非常に誤解を招くものです。スレッドが実行キューの最後に戻される可能性がありますが、待機中のスレッドがあるという保証はないため、yield が削除されたかのように実行し続ける可能性があります。

詳細については、こちらの回答を参照してください。マルチスレッドでの不要な出力

于 2013-09-11T17:51:17.437 に答える
1

javadocを読んでみましょう。

現在のスレッドがプロセッサの現在の使用を放棄する意思があるというスケジューラへのヒント。スケジューラは、このヒントを自由に無視できます。

[...]

この方法を使用することが適切であることはめったにありません。これは、競合状態によるバグの再現に役立つ可能性があるデバッグまたはテストの目的に役立つ場合があります。また、java.util.concurrent.locks パッケージ内のものなどの同時実行制御構造を設計するときにも役立つ場合があります。

の後に別のスレッドがプロセッサを取得することを保証することはできませんyield()。それはスケジューラ次第であり、彼/彼女はあなたの場合にはしたくないようです。sleep()テストのために、代わりに ing を検討することもできます。

于 2013-09-11T17:42:28.497 に答える