$start()$ メソッドは、スレッドの開始に使用されます。この時点で、スレッドは ready になっています。 (Runnable) 状態、実行中ではありません $cpu$ タイム スライスが取得されると、$run()$ メソッドの実行が開始されます。 $run()$ メソッドを直接呼び出すと、クラス内のメソッドが呼び出されるだけであり、このメソッドは基本的に現在のスレッドで実行されるため、$start()$ メソッドを使用して $run()$ を呼び出すことによってのみ実現できます。メソッド。真のマルチスレッド。
@Slf4j(topic = "c.Test4") public class Test4 { public static void main(String[] args) { Thread t1 = new Thread("t1"){ @Override public void run() { log.debug("running"); } }; t1.run(); } }
上記のコードは、$run()$ メソッドへの直接呼び出しです。出力された情報から、$main$ スレッドがこのメソッドを実行したことがわかります。
@Slf4j(topic = "c.Test4") public class Test4 { public static void main(String[] args) { Thread t1 = new Thread("t1"){ @Override public void run() { log.debug("running"); } }; t1.start(); } }
$start()$ メソッドを使用して開始する場合、それは $t1$ スレッドによって実行される実際の $run$ メソッドです。
$Thread$ オブジェクトが $start()$ メソッドを呼び出すと、準備完了状態になることに注意してください。 () を呼び出すことはできません。$ メソッドを呼び出すと、次のコードに示すように、$IllegalThreadStateException$ 例外がスローされます。
@Slf4j(topic = "c.Test4") public class Test4 { public static void main(String[] args) { Thread t1 = new Thread("t1"){ @Override public void run() { log.debug("running"); } }; t1.start(); t1.start(); } }
例外情報:
$sleep()$ メソッドを呼び出すと、現在のスレッドが $Running$ 状態から$Time Waiting$ 状態 (ブロック)
他のスレッドは、$interrupt$ メソッドを使用して、スリープ状態のスレッドに割り込むことができます。このとき、$sleep$ メソッドは InterruptedException## をスローします。
@Slf4j(topic = "c.Test5") public class Test5 { public static void main(String[] args) { Thread t1 = new Thread("t1"){ @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }; t1.start(); log.debug("t1 state {}", t1.getState()); //让主线程休眠500ms try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("t1 state {}", t1.getState()); } } //17:13:21.729 [main] DEBUG c.Test5 - t1 state RUNNABLE //17:13:22.245 [main] DEBUG c.Test5 - t1 state TIMED_WAITING上記のコードでは、最初に $t1$ スレッドが開始されます。このとき、印刷スレッドの状態は$RUNNABLE$ 状態になり、メイン スレッドをスリープ状態にすると、メイン スレッドが最初に印刷を実行することはなくなりますが、まだ $sleep()$ 状態にはなっていません。 $run()$ の $sleep$ メソッドが実行されると、スレッドは $TIMED WAITING$ 状態に入ります。
#
@Slf4j(topic = "c.Test6") public class Thread6 { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread("t1") { @Override public void run() { try { log.debug("enter sleep"); Thread.sleep(2000); } catch (InterruptedException e) { log.debug("wake up"); e.printStackTrace(); } } }; t1.start(); Thread.sleep(1000); log.debug("interrupt t1"); //被唤醒 t1.interrupt(); } }
実行結果
上記のコードでは、$start$ メソッドが開始されると、$t1$ スレッドはスリープ状態に入り、プロンプト情報を出力します。スリープ時間は $2s$ で、$1s$ スリープした後に $t1$ を中断します。 $main$ スレッド内で、スレッドがスリープ状態になると、割り込みメッセージが表示され、$interrupt()$ メソッドが呼び出されます。このとき、スレッドは中断され、例外がスローされます。 $TimeUnit$ クラスは、睡眠のための新しい単位を追加します。これにより読みやすくなりますが、本質的に違いはなく、単位の変換
TimeUnit.SECONDS.sleep(1);//该语句作用是睡眠一秒yield だけです。 $yield$ を呼び出すと、現在のプロセスが $Running$ から $Runnable$ 準備完了状態になり、他のスレッドをスケジュールして実行します。具体的な実装はオペレーティング システムのタスク スケジューラによって異なります。(つまり、タスク スケジューラに他のタスクがない場合、$cpu$ が放棄されてもスレッドは実行を継続します) $sleep$ は実行後にブロック状態に入ります。今回は、$cpu$ はスレッドに割り当てられませんが、$yield$ は準備完了状態に入ります。つまり、他のスレッドを実行する必要がない場合、スレッドにはタイム スライスも割り当てられます。これが最大です。 $sleep$ と $yield$ の違い。スレッド優先度スレッド優先度は、最初にスレッドをスケジュールするようにスケジューラにプロンプトを表示しますが、これは単なるプロンプトであり、スケジューラはそれを無視できます
$cpu$ がビジーな場合、優先度の高い方がより多くのタイム スライスを取得しますが、$cpu$ がアイドル状態の場合、優先度はほとんどありません
while (true) { try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } }$wait$ または条件変数を使用して同様の結果を達成できます効果
違いは、後者の 2 つはロックと対応するウェイクアップ操作を必要とすることです。同期が必要なシナリオに適しています
$sleep$はロック同期が必要ないシナリオに適しています
次のプログラムの結果を出力します:
@Slf4j(topic = "c.Test6") public class Test6 { static int r = 0; public static void main(String[] args) { test(); } private static void test() { log.debug("开始"); Thread t = new Thread("t1") { @Override public void run() { log.debug("开始"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("结束"); r = 10; } }; t.start(); log.debug("r的值是{}", r); log.debug("结束"); } }メイン スレッドと $t1$ スレッドは並列であるため、$t1$ スレッドが $r$ の値を計算するのに $1s$ かかり、メインスレッドは最初に $r$ の値を出力するため、出力される値は 0
解決策:
$t.join();$ を後に追加します。 $t.start();$。 $join$ の機能は、スレッドの実行が終了するのを待つことです。呼び出し元の観点から見ると、結果が返されるのを待ってから実行を続行するのは同期であり、結果が返されるのを待たずに実行を続行するのは非同期です。
打断$sleep, wait, join$的线程,即打断阻塞状态的线程
打断$sleep$的线程,会清空打断状态
@Slf4j(topic = "c.Test7") public class Test7 { public static void main(String[] args) throws InterruptedException { Thread t = new Thread("t1"){ @Override public void run() { log.debug("sleep..."); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } }; t.start(); Thread.sleep(1000); log.debug("interrupt"); t.interrupt(); log.debug("打断标记: {}", t.isInterrupted()); } }
因此我们可以在线程中判断打断标记,来决定是否被打断,以及执行被打断之前的收尾工作。
@Slf4j(topic = "c.Test8") public class Test8 { public static void main(String[] args) throws InterruptedException { Thread t = new Thread("t1"){ @Override public void run() { while (true) { if (Thread.currentThread().isInterrupted()) { log.debug("线程被打断了"); break; } } } }; t.start(); Thread.sleep(1000); log.debug("interrupt"); t.interrupt(); } }
默认情况下,$java$需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其他非守护线程运行结束了,即使守护线程的代码没有执行完毕,也会强制结束。
@Slf4j(topic = "c.Test10") public class Test10 { public static void main(String[] args) throws InterruptedException { Thread t = new Thread("t1") { @Override public void run() { while (true) { } } }; //设置线程为守护线程 t.setDaemon(true); t.start(); Thread.sleep(1000); log.debug("主线程结束"); } }
如果不把$t$设置为守护线程,则因为线程内部的死循环,导致程序不会结束运行。
以上がJavaスレッドでstartメソッドとrunメソッドを使用する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。