yield()の主な用途は何ですか?join()やinterrupt()とどのように違うのですか?


106

yield()Java でのメソッドの使用について、特に以下のコード例で少し混乱しています。また、yield()は「スレッドの実行を防止するために使用される」ことも読みました。

私の質問は:

  1. 以下のコードは、使用時と使用yield()しない時の両方で同じ出力になると思います。これは正しいです?

  2. 実際、主な用途はyield()何ですか?

  3. およびメソッドとはどのようにyield()異なりますか?join()interrupt()

コード例:

public class MyRunnable implements Runnable {

   public static void main(String[] args) {
      Thread t = new Thread(new MyRunnable());
      t.start();

      for(int i=0; i<5; i++) {
          System.out.println("Inside main");
      }
   }

   public void run() {
      for(int i=0; i<5; i++) {
          System.out.println("Inside run");
          Thread.yield();
      }
   }
}

上記のコードを使用しても使用しなくても同じ出力が得られますyield()

Inside main
Inside main
Inside main
Inside main
Inside main
Inside run
Inside run
Inside run
Inside run
Inside run

この質問は広すぎるため、締めくくる必要があります。
Raedwald 2014

いいえyield()。持っている場合と持っていない場合、同じ結果は返されません。5ではなくiが大きい場合は、yield()メソッドの効果を確認できます。
ラクシュマン2014

回答:


97

出典:http : //www.javamex.com/tutorials/threads/yield.shtml

ウィンドウズ

Hotspot実装では、Thread.yield()Java 5とJava 6の間で動作方法が変更されました。

Java 5ではThread.yield()、Windows API呼び出しを呼び出しますSleep(0)。これには、現在のスレッドのクォンタムクリアし、優先度のキュー最後に置くという特殊な効果があります。言い換えると、同じ優先順位(およびより高い優先順位のスレッド)のすべての実行可能なスレッドは、生成されたスレッドが次にCPU時間を与えられる前に実行する機会を得ます。最終的に再スケジュールされると、フルクォンタムが返されますが、生成された時点から残っているクォンタムは「引き継がれません」。この動作は、スリープしていないスレッドが一般に1クォンタム値(実質的には、10または15ミリ秒のティックの1/3)を失うゼロ以外のスリープとは少し異なります。

Java 6では、この動作が変更されました。Hotspot VMはThread.yield()、Windows SwitchToThread()API呼び出しを使用して実装さ れるようになりました。この呼び出しにより、現在のスレッド現在のタイムスライスを放棄しますが、量子全体は放棄しません。これは、他のスレッドの優先度に応じて、譲るスレッドを後で1つの割り込み期間にスケジュールすることができることを意味します。(タイムスライスの詳細については、スレッドのスケジューリングに関するセクションを参照してください。)

Linux

Linuxでは、Hotspotは単にを呼び出しますsched_yield()。この呼び出しの結果は少し異なり、おそらくWindowsの場合よりも深刻です。

  • 生成されたスレッドは他のすべてのスレッドがCPUのスライスを持つまで、CPUの別のスライスを取得しません。
  • (少なくともカーネル2.6.8以降)、スレッドが生成したという事実は、スケジューラの最近のCPU割り当てに関するヒューリスティックによって暗黙的に考慮されます。したがって、暗黙的に、生成されたスレッドは、未来。

(優先順位とスケジューリングアルゴリズムの詳細については、スレッドスケジューリングのセクションを参照してください。)

いつ使用するのyield()ですか?

実質的には決して言えないと思います。その動作は標準的に定義されておらず、yield()を使用して実行する可能性のあるタスクを実行するための一般的なより良い方法があります。

  • CPUの一部のみ使用しようとしている場合は、スレッドが最後の処理チャンクで使用したCPUの量を見積もり、それを補正するために一定時間スリープすることで、より制御可能な方法でこれを行うことができます。睡眠()メソッド。
  • プロセスまたはリソースの完了または使用可能になるのを待機している場合は、join()を使用して別のスレッドが完了するのを待機する、待機/通知メカニズムを使用して1つのスレッドを許可するなど、より効率的な方法があります。タスクが完了したこと、または理想的にはセマフォブロッキングキューなどのJava 5同時実行構造の1つを使用して、別のタスクに通知する。

18
「残りの量子」、「全体の量子」-誰かが「量子」という言葉の意味を忘れていた途中のどこか
kbolino

@kbolino Quantumが新しいアトムです。
Evgeni Sergeev 2016

2
@kbolino-... ラテン語:「同じくらい」、「どれだけ」。これが上記の使用とどのように矛盾しているかはわかりません。言葉は単に説明された量を意味するだけなので、使用済みと残りの部分に分けることは、私には完全に合理的に思えます。
Periata Breatta 2016年

@PeriataBreatta物理学以外の単語に慣れている方が理にかなっていると思います。私が知っていたのは物理学の定義だけでした。
kbolino 2016年

私はこの質問に賞金をかけ、この回答を7、8、9に更新しました。7、8、8に関する現在の情報で編集すると、賞金を獲得できます。

40

質問は賞金で再活性化されたようyieldです。今、実用的な用途は何かを尋ねています。私の経験から例を挙げましょう。

ご存知のyieldように、別のスレッドが実行されるようにスケジュールできるように、呼び出しスレッドが実行しているプロセッサを強制的に放棄します。これは、現在のスレッドが今のところ作業を終了しているが、すぐにキューの先頭に戻り、何らかの条件が変更されたかどうかを確認する場合に役立ちます。これは条件変数とどう違うのですか?yieldスレッドが実行状態にはるかに速く戻ることができます。条件変数で待機している場合、スレッドは中断され、別のスレッドが続行する必要があることを通知するまで待機する必要があります。yield基本的には、「別のスレッドの実行を許可しますが、状態が非常に速く変化すると予想されるので、すぐに作業に戻ることができます」と述べています。これは、状態が急速に変化する可能性がありますが、スレッドを一時停止するとパフォーマンスが大幅に低下するビジースピンを示唆しています。

しかし、十分な説明があります。具体的な例を次に示します。波面の平行パターンです。この問題の基本的な例は、0と1で満たされた2次元配列で1の個々の「島」を計算することです。「島」とは、垂直方向または水平方向に互いに隣接するセルのグループです。

1 0 0 0
1 1 0 0
0 0 0 1
0 0 1 1
0 0 1 1

ここには、1の2つのアイランドがあります。左上と右下です。

簡単な解決策は、配列全体に最初のパスを作成し、1の値をインクリメントカウンターに置き換えて、最後に各1が行のメジャーオーダーのシーケンス番号に置き換えられるようにすることです。

1 0 0 0
2 3 0 0
0 0 0 4
0 0 5 6
0 0 7 8

次のステップでは、各値は、それ自体とその近傍の値の間の最小値に置き換えられます。

1 0 0 0
1 1 0 0
0 0 0 4
0 0 4 4
0 0 4 4

これで、2つの島があることを簡単に判断できます。

並列実行したい部分は、最小値を計算するステップです。詳細には触れずに、各スレッドはインターリーブ方式で行を取得し、上の行を処理するスレッドによって計算された値に依存します。したがって、各スレッドは、前の行を処理するスレッドよりも少し遅れる必要がありますが、妥当な時間内に追いつく必要もあります。詳細と実装については、このドキュメントで説明します。その使用法がsleep(0)多かれ少なかれCと同等のことに注意してくださいyield

この場合yield、各スレッドを順番に一時停止させるために使用されましたが、隣接する行を処理するスレッドはその間非常に速く進むため、条件変数は悲惨な選択を示します。

ご覧のとおり、これyieldは非常に細かい最適化です。あまり変化しない条件で待機するなど、間違った場所で使用すると、CPUが過度に使用されます。

長いおしゃべりでごめんなさい、はっきりさせてほしい。


1
IIUCはあなたがドキュメントに提示するもの、つまり、この場合、ビジー待機の方が効率的でありyield、他のスレッドが計算を続行する機会を他のスレッドに与えるために条件が満たされないときに呼び出すよりも、レベル同期プリミティブですね
PetrPudlák2014

3
@PetrPudlák:はい。これとスレッドシグナリングのベンチマークの比較を行ったところ、この場合のパフォーマンスの差は非常に大きかった。条件が非常に速くtrueになる可能性があるため(これが重要な問題です)、スレッドをOSによって保留にすると、を使用してCPUを非常に短い時間で放棄するのではなく、条件変数が遅すぎyieldます。
チューダー

@チューダー素晴らしい説明!
開発者MariusŽilėnas16年

1
「Cの歩留まりとほぼ同じであるsleep(0)の使用方法に注意してください。」まあ、Javaでsleep(0)が必要な場合は、なぜそれを使用しないのですか?Thread.sleep()はすでに存在するものです。この回答がThread.sleep(0)の代わりにThread.yield()を使用する理由についての理由を提供するかどうかはわかりません。それらが異なる理由を説明する既存のスレッドもあります。
e's

@eis:Thread.sleep(0)対Thread.yield()はこの回答の範囲を超えています。私はCで同等のものを探している人々のためにThread.sleep(0)についてのみ言及していました。問題はThread.yield()の使用についてでした。
チューダー

12

違いについてyield()interrupt()およびjoin()-一般的にだけでなく、Javaで:

  1. 降伏:文字通り、「降伏する」とは、手放す、放棄する、降伏することを意味します。譲歩するスレッドは、代わりに他のスレッドをスケジュールできるようにしたいオペレーティングシステム(または仮想マシンなど)に通知します。これは、あまり重要ではないことを示しています。ただし、これは単なるヒントであり、効果があるとは限りません。
  2. 結合:複数のスレッドがハンドル、トークン、またはエンティティで「結合」する場合、すべてのスレッドは、他のすべての関連スレッドが実行を完了するまで待機します(全体または対応する結合まで)。つまり、多数のスレッドがすべてのタスクを完了しています。次に、これらのスレッドのそれぞれをスケジュールして、他の作業を続行することができます。これらのタスクはすべて実際に完了していると想定できます。(SQL結合と混同しないでください!)
  3. 割り込み:あるスレッドが、スリープ中、待機中、または参加中の別のスレッドを「突く」ために使用します。これにより、おそらく実行が中断された可能性があるため、再度実行を継続するようにスケジュールされます。(ハードウェア割り込みと混同しないでください!)

特にJavaについては、

  1. 接合:

    Thread.joinの使用方法 (ここではStackOverflow)

    いつスレッドに参加するのですか?

  2. 収量:

  3. 中断:

    Thread.interrupt()は悪ですか?(ここではStackOverflow)


ハンドルまたはトークンに参加するとはどういう意味ですか?wait()およびnotify()メソッドはObjectにあり、ユーザーは任意のObjectで待機できます。しかし、join()は抽象性が低く、続行する前に終了する特定のスレッドで呼び出す必要があります...
spaaarky21 2014年

@ spaaarky21:つまり、Javaである必要はありません。また、a wait()は結合ではなく、呼び出しスレッドが取得しようとしているオブジェクトのロックに関するものです。ロックは他のユーザーによってロックが解放され、スレッドによって取得されるまで待機します。それに応じて私の答えを微調整しました。
einpoklum 2014年

10

まず、実際の説明は

現在実行中のスレッドオブジェクトを一時停止して、他のスレッドの実行を許可します。

現在、メインスレッドがrun新しいスレッドのメソッドが実行される前に5回ループを実行する可能性が非常に高いため、へのすべての呼び出しyieldは、メインスレッドのループが実行された後にのみ発生します。

joinで呼び出されているスレッドのjoin()実行が完了するまで、現在のスレッドを停止します。

interrupt呼び出されているスレッドに割り込み、InterruptedExceptionを引き起こします。

yield 他のスレッドへのコンテキスト切り替えを許可するため、このスレッドはプロセスのCPU使用率全体を消費しません。


+1。また、yield()を呼び出した後、同じ優先度のスレッドのプールが与えられた場合、同じスレッドが実行のために再び選択されないという保証はありません。
Andrew Fielden、2011

しかし、 SwitchToThread()コールは、より良い睡眠(0)よりも、これは:)のJavaのバグでなければなりません
ПетърПетров

4

現在の回答は最新ではないため、最近の変更を考慮して改訂が必要です。

6から9までは、Javaのバージョンに実際的な違いはありませんThread.yield()

TL; DR;

OpenJDKソースコードに基づく結論(http://hg.openjdk.java.net/)。

USDTプローブのHotSpotサポート(システムトレース情報はdtraceガイドで説明されています)とJVMプロパティを考慮しない場合ConvertYieldToSleep、ソースコードyield()はほとんど同じです。以下の説明を参照してください。

Java 9

Thread.yield()OS固有のメソッドを呼び出すos::naked_yield()
Linuxの場合:

void os::naked_yield() {
    sched_yield();
}

Windowsの場合:

void os::naked_yield() {
    SwitchToThread();
}

Java 8以前:

Thread.yield()OS固有のメソッドを呼び出すos::yield()
Linuxの場合:

void os::yield() {
    sched_yield();
}

Windowsの場合:

void os::yield() {  os::NakedYield(); }

ご覧のとおり、Thread.yeald()LinuxではすべてのJavaバージョンで同じです。JDK 8の
Windowsを見てみましょうos::NakedYield()

os::YieldResult os::NakedYield() {
    // Use either SwitchToThread() or Sleep(0)
    // Consider passing back the return value from SwitchToThread().
    if (os::Kernel32Dll::SwitchToThreadAvailable()) {
        return SwitchToThread() ? os::YIELD_SWITCHED : os::YIELD_NONEREADY ;
    } else {
        Sleep(0);
    }
    return os::YIELD_UNKNOWN ;
}

Win32 APIのSwitchToThread()メソッドの存在の追加チェックにおけるJava 9とJava 8の違い。同じコードがJava 6にも存在します。JDK7の
ソースコードos::NakedYield()は少し異なりますが、動作は同じです。

    os::YieldResult os::NakedYield() {
    // Use either SwitchToThread() or Sleep(0)
    // Consider passing back the return value from SwitchToThread().
    // We use GetProcAddress() as ancient Win9X versions of windows doen't support SwitchToThread.
    // In that case we revert to Sleep(0).
    static volatile STTSignature stt = (STTSignature) 1 ;

    if (stt == ((STTSignature) 1)) {
        stt = (STTSignature) ::GetProcAddress (LoadLibrary ("Kernel32.dll"), "SwitchToThread") ;
        // It's OK if threads race during initialization as the operation above is idempotent.
    }
    if (stt != NULL) {
        return (*stt)() ? os::YIELD_SWITCHED : os::YIELD_NONEREADY ;
    } else {
        Sleep (0) ;
    }
    return os::YIELD_UNKNOWN ;
}

追加のチェックはSwitchToThread()、Windows XPおよびWindows Server 2003以降で利用できる方法により削除されました(msdnのメモを参照)。


2

実際、yield()の主な用途は何ですか?

Yieldは、現在のスレッドを停止して、より高い優先度でスレッドの実行を開始できることをCPUに示唆しています。つまり、現在のスレッドに低い優先度の値を割り当てて、より重要なスレッドのための余地を残します。

以下のコードは、yield()を使用した場合と使用しない場合の両方で同じ出力になると思います。これは正しいです?

いいえ、2つは異なる結果を生成します。yield()がなければ、スレッドが制御を取得すると、「内部実行」ループが一度に実行されます。ただし、yield()を使用すると、スレッドが制御を取得すると、「内部実行」が1回出力され、制御が他のスレッドに渡されます(存在する場合)。保留中のスレッドがない場合、このスレッドは再開されます。したがって、「Inside run」が実行されるたびに、実行する他のスレッドが検索され、使用可能なスレッドがない場合は、現在のスレッドが実行を続けます。

yield()がjoin()およびinterrupt()メソッドとどのように異なるのですか?

yield()は他の重要なスレッドにスペースを与えるためのものであり、join()は別のスレッドの実行が完了するのを待つためのもので、interrupt()は現在実行中のスレッドに割り込んで他のことをするためのものです。


このステートメントが当てはまるWithout a yield(), once the thread gets control it will execute the 'Inside run' loop in one goかどうかを確認したいだけですか?どうか明らかにしてください。
Abdullah Khan

0

Thread.yield()スレッドを「実行中」状態から「実行可能」状態にします。注:スレッドが「待機」状態になることはありません。


@PJMeisch、インスタンスのRUNNING状態はありませんjava.lang.Thread。ただし、これは、Threadインスタンスがプロキシであるネイティブスレッドのネイティブの「実行中」状態を排除するものではありません。
ソロモンスロー

-1

Thread.yield()

Thread.yield()メソッドを呼び出すと、スレッドスケジューラは現在実行中のスレッドをRunnable状態に維持し、同じ優先度またはより高い優先度の別のスレッドを選択します。同等でより高い優先度のスレッドがない場合は、呼び出しのyield()スレッドを再スケジュールします。yieldメソッドはスレッドを待機またはブロック状態にしないことに注意してください。スレッドは、実行状態から実行可能状態にしか作成できません。

join()

スレッドインスタンスによってjoinが呼び出されると、このスレッドは、現在実行中のスレッドに、Joiningスレッドが完了するまで待機するように通知します。結合は、現在のタスクが完了する前に完了する必要があるタスクで使用されます。


-4

yield()の主な用途は、マルチスレッドアプリケーションを保留することです。

これらすべてのメソッドの違いは、yield()が別のスレッドの実行中にスレッドを保留にし、そのスレッドの完了後に戻ることです。join()は、スレッドの開始を一緒に実行し、そのスレッドの終了後に別のスレッドを実行します。終了すると、interrupt()はしばらくの間スレッドの実行を停止します。


お返事ありがとうございます。ただし、他の回答がすでに詳細に説明していることを繰り返すだけです。使用yieldすべき適切なユースケースの報奨金を提供します。
PetrPudlák14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.