やあ!今日もマルチスレッドについてお話します。Thread クラスとそのいくつかのメソッドが何を行うかを調べてみましょう。以前にクラス メソッドを学習したときは、通常、<メソッド名> -> <メソッドの動作> と記述するだけでした。 これは のメソッドでは機能しません
Thread
:) それらには、いくつかの例がなければ理解できないより複雑なロジックがあります。
Thread.start() メソッド
まずは繰り返してみましょう。Thread
おそらく覚えていると思いますが、クラスを継承させてメソッドをオーバーライドすることで、スレッドを作成できますrun()
。しかし、もちろんそれ自体は始まりません。これを行うには、オブジェクトのstart()
メソッドを呼び出します。 前のレッスンの例を思い出してみましょう。
public class MyFirstThread extends Thread {
@Override
public void run() {
System.out.println("Thread executed: " + getName());
}
}
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyFirstThread thread = new MyFirstThread();
thread.start();
}
}
}
注:スレッドを開始するには、start()
ではなく特別なメソッドrun()
。これは、特にマルチスレッドの勉強を始めたばかりの場合に起こりやすい間違いです。run()
この例では、の代わりにメソッドを 10 回呼び出すとstart()
、次のようになります。
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyFirstThread thread = new MyFirstThread();
thread.run();
}
}
}
プログラムの結果を見てください: 実行されたスレッド: Thread-0 実行されたスレッド: Thread-1 実行されたスレッド: Thread-2 実行されたスレッド: Thread-3 実行されたスレッド: Thread-4 実行されたスレッド: Thread-5 実行されたスレッド: Thread-6実行されたスレッド: スレッド-7 実行されたスレッド: スレッド-8 実行されたスレッド: スレッド-9 出力の順序を見てください: すべてが完璧な順序で起こっています。変ですよね?私たちはこれに慣れていません。なぜなら、スレッドが開始および実行される順序が、オペレーティング システム内の優れた知性、つまりスレッド スケジューラによって決定されることをすでに知っているからです。もしかしたら、私たちはただ幸運だったのかもしれません?もちろん、これは運の問題ではありません。プログラムをさらに数回実行することで、これを確認できます。問題は、run()
このメソッドはマルチスレッドとは直接関係がありません。この場合、プログラムはメソッドを実行するスレッドと同じメインスレッドで実行されますmain()
。コンソールに 10 行を連続して出力するだけです。10 個のスレッドが開始されていません。したがって、今後はこのことを思い出し、常に自分自身をチェックしてください。run()
メソッドを呼び出したい場合は、 を呼び出しますstart()
。さらに進んでみましょう。
Thread.sleep() メソッド
現在のスレッドの実行をしばらく一時停止するには、sleep()
メソッドを使用します。 このsleep()
メソッドは、スレッドをスリープ状態にする時間を示すミリ秒数を引数として受け取ります。
public class Main {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
Thread.sleep(3000);
System.out.println(" - How long did I sleep? \n - " + ((System.currentTimeMillis()-start)) / 1000 + " seconds");
}
}
コンソール出力: - どのくらい寝ましたか? - 3 秒 注:このsleep()
メソッドは静的です。現在のスレッドをスリープさせます。つまり、現在実行中のものです。ここでもう 1 つの重要な点があります。スリープ中のスレッドは中断される可能性があります。この場合、プログラムは をスローしますInterruptedException
。以下に例を考えてみましょう。ところで、スレッドが起きた後はどうなるのでしょうか?中断したところから引き続き実行されますか? Thread.sleep()
いいえ。スレッドが起動した後、つまり引数として渡された時間が経過した後、スレッドは実行可能状態に移行します。州。ただし、これはスレッド スケジューラが実行するという意味ではありません。他の非スリープスレッドを優先して、新しく目覚めたスレッドが少し遅れて作業を継続できるようにする可能性が十分にあります。必ず覚えておいてください。起きたからといって、すぐに仕事を続けるわけではありません。
Thread.join() メソッド
このjoin()
メソッドは、別のスレッドが終了するまで現在のスレッドの実行を一時停止します。2 つのスレッドがある場合、t1
とt2
、そして次のように書きます。
t1.join()
その後、作業が完了するt2
まで開始されません。t1
このjoin()
メソッドは、スレッドの実行順序を保証するために使用できます。join()
次の例で このメソッドがどのように機能するかを考えてみましょう。
public class ThreadExample extends Thread {
@Override
public void run() {
System.out.println("Thread started: " + getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread " + getName() + " is finished.");
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
ThreadExample t1 = new ThreadExample();
ThreadExample t2 = new ThreadExample();
t1.start();
/* The second thread (t2) will start running only after the first thread (t1)
is finished (or an exception is thrown) */
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
// The main thread will continue running only after t1 and t2 have finished
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All threads have finished. The program is finished.");
}
}
簡単なThreadExample
クラスを作成しました。そのタスクは、スレッドが開始されたことを示すメッセージを表示し、5 秒間スリープ状態になり、最後に作業が完了したことを報告することです。ケーキの一部。メインのロジックはMain
クラス内にあります。コメントを見てください。join()
スレッドの実行順序を適切に管理するためにこのメソッドを使用しています。このトピックを始めた経緯を覚えていると思いますが、実行順序はスレッド スケジューラによって処理されます。スレッドは独自の裁量で、毎回異なる方法で実行されます。t1
ここでは、スレッドが最初に開始されて実行されることを保証するメソッドを使用しています。t2
スレッドが終了し、その後のみプログラムのメインスレッドが続行されます。次に進みます。実際のプログラムでは、スレッドの実行を中断する必要がある状況がよくあります。たとえば、スレッドは実行中ですが、特定のイベントまたは条件を待っているとします。これが発生すると、スレッドが停止します。何らかのstop()
方法があれば、それはおそらく意味があるでしょう。しかし、それはそれほど単純ではありません。かつて、Java には実際にメソッドがありThread.stop()
、スレッドを中断することができました。しかし、後に Java ライブラリから削除されました。Oracle のドキュメントでこれを見つけると、非推奨としてマークされていることがわかります。。なぜ?他に何もせずにスレッドを停止しただけだからです。たとえば、スレッドがデータを操作して何かを変更している可能性があります。そして、その作業の途中で、突然、そして無礼にそのstop()
方法によって切断されました。適切なシャットダウンも、リソースの解放も、さらにはエラー処理もなければ、そのようなことはありませんでした。少し誇張して言えば、このstop()
方法は邪魔なものをすべて破壊しただけです。それは、コンピューターの電源を切るためにコンセントから電源コードを抜くようなものでした。はい、期待通りの結果が得られます。しかし、数週間後にはコンピューターがそのように扱ってくれたことに感謝しなくなることは誰もが知っています。そのため、Java ではスレッドを中断するためのロジックが変更され、特別なinterrupt()
メソッドが使用されるようになりました。
Thread.interrupt() メソッド
interrupt()
メソッドがスレッド上で呼び出された場合はどうなりますか? 可能性は 2 つあります。
- たとえば、
join
またはsleep
メソッドによってオブジェクトが待機状態にあった場合、待機は中断され、プログラムは をスローしますInterruptedException
。 - スレッドが機能状態にあった場合、ブール値
interrupted
フラグがオブジェクトに設定されます。
Thread
クラスにはメソッドがありますboolean isInterrupted()
。基礎コースのレッスンにあった時計の例に戻りましょう。便宜上、少し簡略化しました。
public class Clock extends Thread {
public static void main(String[] args) throws InterruptedException {
Clock clock = new Clock();
clock.start();
Thread.sleep(10000);
clock.interrupt();
}
public void run() {
Thread current = Thread.currentThread();
while (!current.isInterrupted())
{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("The thread was interrupted");
break;
}
System.out.println("Tick");
}
}
}
この場合、時計が開始され、1 秒ごとに時を刻み始めます。10 秒目で、時計のスレッドを中断します。 すでにご存知のとおり、中断しようとしているスレッドが待機状態のいずれかにある場合、結果は ですInterruptedException
。これはチェック例外であるため、簡単にキャッチしてロジックを実行してプログラムを終了できます。そしてそれがまさに私たちがやったことなのです。結果は次のとおりです。 Tick Tick Tcik Tick Tick Tick Tick Tick スレッドが中断されました これで、Thread
クラスの最も重要なメソッドの紹介は終わりです。幸運を!
GO TO FULL VERSION