ホームページ >Java >&#&チュートリアル >Javaスレッドのyieldメソッドとjoinメソッドの違い
長い間、マルチスレッドの質問は面接官に好まれてきました。私個人としては、複雑なマルチスレッド アプリケーションを開発する機会を実際に得ている人はほとんどいないと考えていますが (私は過去 7 年間で開発したことがあります)、マルチスレッドを理解することは自信を高めるのに役立ちます。前回は、wait() メソッドと sleep() メソッドの違いについて説明しました。今回は、join() メソッドと yield() メソッドの違いについて説明します。正直に言うと、私はこれらの方法を実際に使用したことはありませんので、不適切な点があると感じた場合は議論してください。
Java スレッドのスケジューリングに関する背景知識
さまざまなスレッドにおいて、Java 仮想マシンは優先順位付きの優先順位ベースのスケジューラを実装する必要があります。これは、Java プログラム内の各スレッドに、定義された範囲内の正の整数で表される特定の優先順位が割り当てられることを意味します。優先順位は開発者が変更できます。スレッドが一定期間実行されている場合でも、Java 仮想マシンはその優先順位を変更しません。これは、Java 仮想マシンと基礎となるオペレーティング システムの間の合意により、オペレーティング システムが優先順位を選択する必要があるためです。最も優先度の高い Java スレッドが実行されます。したがって、Java は優先順位ベースのスケジューラを実装していると言えます。スケジューラは優先順位に従って実装されます。つまり、優先順位の高いスレッドが到着すると、優先順位の低いスレッドが実行中かどうかに関係なく、スレッドは中断 (プリエンプト) されます。この規則はオペレーティング システムに常に当てはまるわけではありません。つまり、オペレーティング システムは、優先順位の低いスレッドの実行を選択する場合があります。 (私はマルチスレッドのこの側面が嫌いです。なぜなら、それは何も保証しないからです)
Java ではタイム スライスでのスレッドの実行を制限していませんが、ほとんどのオペレーティング システムでは制限されていることに注意してください。用語におけるよくある混乱: プリエンプションはタイム スライシングと混同されることがよくあります。実際、プリエンプションとは、優先度の高いスレッドのみが優先度の低いスレッドよりも前に実行できることを意味しますが、スレッドの優先度が同じ場合、スレッドは相互にプリエンプトすることはできません。通常、これらはタイムスライスされますが、これは Java の要件ではありません。
スレッドの優先順位を理解する
次に、スレッドの優先順位を理解することは、マルチスレッド学習、特に yield() 関数の動作プロセスを理解する上で重要なステップです。
スレッドの優先順位が指定されていない場合、すべてのスレッドが通常の優先順位を持つことに注意してください。
優先度は1〜10の範囲で指定できます。 10 は最高の優先順位を表し、1 は最低の優先順位を表し、5 は通常の優先順位を表します。
実行時には、優先度が最も高いスレッドが優先されることに注意してください。ただし、スレッドが開始されたときに実行状態になるという保証はありません。
現在実行中のスレッドは、スレッド プール内で実行の機会を待っているスレッドよりも常に高い優先順位を持つ可能性があります。
どのスレッドを実行するかはスケジューラが決定します。
t.setPriority() はスレッドの優先度を設定するために使用されます。
スレッドの優先順位は、スレッド開始メソッドが呼び出される前に設定する必要があることに注意してください。
MIN_PRIORITY、MAX_PRIORITY、NORM_PRIORITY などの定数を使用して優先度を設定できます
さて、スレッドのスケジューリングとスレッドの優先度をある程度理解したら、本題に入りましょう。
yield() メソッド
理論的には、yield は手放す、諦める、降伏することを意味します。 yield() メソッドを呼び出すスレッドは、仮想マシンに他のスレッドにその位置を占有させる意思があることを伝えます。これは、スレッドが緊急なことを行っていないことを示します。これは単なるヒントであり、影響がないことを保証するものではないことに注意してください。
yield() は Thread.java で次のように定義されています:
/** * A hint to the scheduler that the current thread is willing to yield its current use of a processor. The scheduler is free to ignore * this hint. Yield is a heuristic attempt to improve relative progression between threads that would otherwise over-utilize a CPU. * Its use should be combined with detailed profiling and benchmarking to ensure that it actually has the desired effect. */ public static native void yield();
上記の定義に関する重要な点をいくつかリストしてみましょう:
Yield は静的ネイティブ メソッドです
Yield は現在実行中のスレッドに次のことを伝えます 実行の機会が与えられますスレッド プール内の同じ優先順位を持つスレッドに割り当てられます。
Yield は、現在実行中のスレッドがすぐに実行可能な状態に移行することを保証するものではありません
スレッドを待機状態またはブロック状態ではなく、実行状態から実行可能な状態に移行させることのみが可能です
yield() メソッドの使用法例
在下面的示例程序中,我随意的创建了名为生产者和消费者的两个线程。生产者设定为最小优先级,消费者设定为最高优先级。在Thread.yield()注释和非注释的情况下我将分别运行该程序。没有调用yield()方法时,虽然输出有时改变,但是通常消费者行先打印出来,然后事生产者。
调用yield()方法时,两个线程依次打印,然后将执行机会交给对方,一直这样进行下去。
package test.core.threads; public class YieldExample { public static void main(String[] args) { Thread producer = new Producer(); Thread consumer = new Consumer(); producer.setPriority(Thread.MIN_PRIORITY); //Min Priority consumer.setPriority(Thread.MAX_PRIORITY); //Max Priority producer.start(); consumer.start(); } } class Producer extends Thread { public void run() { for (int i = 0; i < 5; i++) { System.out.println("I am Producer : Produced Item " + i); Thread.yield(); } } } class Consumer extends Thread { public void run() { for (int i = 0; i < 5; i++) { System.out.println("I am Consumer : Consumed Item " + i); Thread.yield(); } } }
上述程序在没有调用yield()方法情况下的输出:
I am Consumer : Consumed Item 0 I am Consumer : Consumed Item 1 I am Consumer : Consumed Item 2 I am Consumer : Consumed Item 3 I am Consumer : Consumed Item 4 I am Producer : Produced Item 0 I am Producer : Produced Item 1 I am Producer : Produced Item 2 I am Producer : Produced Item 3 I am Producer : Produced Item 4
上述程序在调用yield()方法情况下的输出:
I am Producer : Produced Item 0 I am Consumer : Consumed Item 0 I am Producer : Produced Item 1 I am Consumer : Consumed Item 1 I am Producer : Produced Item 2 I am Consumer : Consumed Item 2 I am Producer : Produced Item 3 I am Consumer : Consumed Item 3 I am Producer : Produced Item 4 I am Consumer : Consumed Item 4
join()方法
线程实例的方法join()方法可以使得在另一个线程的执行结束后再开始执行这个线程。如果join()方法被在一个线程实例上调用,当前运行着的线程将阻塞直到线程实例完成了执行。
//Waits for this thread to die. public final void join() throws InterruptedException
在join()方法内设定超时,使得join()方法的影响在特定超时后无效。当超时时,主方法和任务线程申请运行的时候是平等的。然而,当涉及sleep时,join()方法依靠操作系统计时,所以你不应该假定join()方法将会等待你指定的时间。
像sleep,join通过抛出InterruptedException对中断做出回应。
join()方法使用示例
package test.core.threads; public class JoinExample { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(new Runnable() { public void run() { System.out.println("First task started"); System.out.println("Sleeping for 2 seconds"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("First task completed"); } }); Thread t1 = new Thread(new Runnable() { public void run() { System.out.println("Second task completed"); } }); t.start(); // Line 15 t.join(); // Line 16 t1.start(); } } Output: First task started Sleeping for 2 seconds First task completed Second task completed