JavaのThreadクラスを完全ガイド!初心者でもわかるスレッドの基本と使い方
生徒
「Javaで同時に複数の処理を実行したいんですけど、どうすればいいですか?」
先生
「その場合はThreadクラスを使うと便利です。Javaのjava.langパッケージに含まれていて、並行処理を実現できますよ。」
生徒
「Threadクラスって具体的にどうやって使うんですか?」
先生
「それでは、初心者向けにThreadクラスの基本から実際の使い方まで丁寧に説明していきましょう。」
1. Threadクラスとは?
JavaのThreadクラスは、ひとつのプログラムの中で「作業の手分け」をするための仕組みです。軽い実行の流れ(=スレッド)を複数用意し、同じ時間帯に別々の処理を進められます。Threadはjava.langに含まれているので特別なインポートは不要。ゲームの画面描画と入力処理、Web処理のバックグラウンド作業など、同時進行が自然に書けるのが特徴です。
まずは「スレッドが複数の流れで動く」感覚だけつかみましょう。次のサンプルは、メインの流れ(mainメソッド)とは別に、もう1本のスレッドを1つ起動して、どちらの名前で動いているかを表示するだけのものです。
public class WhatIsThread {
public static void main(String[] args) {
// いま動いているスレッド(=メインスレッド)の名前を表示
System.out.println("メイン: " + Thread.currentThread().getName());
// もう1本のスレッドを用意して、ひと言表示するだけの仕事を任せる
Thread worker = new Thread(() -> {
System.out.println("ワーカー: " + Thread.currentThread().getName());
});
// スレッドの開始(メインとは別の流れが動き出す)
worker.start();
// メインの流れもそのまま続く
System.out.println("メインは続行中");
}
}
メイン: main
メインは続行中
ワーカー: Thread-0
出力の行順は環境によって前後します(別々の流れで進むため)。ここでは「main」とは別名のスレッドが動いている、と分かればOKです。使い方の詳細や実装パターンは、このあと順を追って紹介していきます。
2. Threadクラスを使う基本的な方法
Threadを使う代表的な道は2つあります。① Threadクラスを継承してrun()を上書きする方法、② Runnableインターフェースを実装してThreadに渡す方法です。まずは動きが見えやすい①で、新しい流れを開始するにはstart()を呼ぶという点だけしっかり押さえましょう(run()を直接呼ぶと別スレッドにならず、ただのメソッド呼び出しになります)。
以下は最小の例です。メインの流れとは別に、もう1本のスレッドがカウントを表示します。途中でThread.currentThread().getName()を出力して、どのスレッドが動いているかも確認します。
class MyThread extends Thread {
@Override
public void run() {
// ここが「別スレッド」で実行される処理
System.out.println("子: " + Thread.currentThread().getName());
for (int i = 1; i <= 3; i++) {
System.out.println("子のカウント: " + i);
}
}
}
public class ThreadExample {
public static void main(String[] args) {
System.out.println("メイン: " + Thread.currentThread().getName());
MyThread thread = new MyThread();
// 新しいスレッドを開始(runはstartの内部で呼ばれる)
thread.start();
// メインの処理はそのまま進むことがある(順番は前後しうる)
System.out.println("メインは続行中");
}
}
メイン: main
メインは続行中
子: Thread-0
子のカウント: 1
子のカウント: 2
子のカウント: 3
出力の順序は実行環境により前後しますが、「start()で別の流れが動き出す」「run()は直接呼ばない」という2点を理解できれば十分です。次のセクションではもう一方の書き方(Runnable)を扱います。
3. Runnableインターフェースを使った方法
Runnableを使うとクラスの継承制限を回避できるため、こちらが主流です。Runnableを実装したクラスをThreadに渡して実行する仕組みです。
class MyTask implements Runnable {
@Override
public void run() {
System.out.println("Runnableでスレッドを実行中");
}
}
public class RunnableExample {
public static void main(String[] args) {
Thread thread = new Thread(new MyTask());
thread.start();
}
}
Runnableでスレッドを実行中
4. 複数スレッドの同時実行
Threadクラスを使えば、複数のスレッドを同時に走らせることが可能です。例えば二つのタスクを並行で実行すると、処理が順番に進むのではなく並行して進む様子を確認できます。
public class MultiThreadExample {
public static void main(String[] args) {
Runnable task1 = () -> {
for (int i = 0; i < 5; i++) {
System.out.println("タスク1: " + i);
}
};
Runnable task2 = () -> {
for (int i = 0; i < 5; i++) {
System.out.println("タスク2: " + i);
}
};
new Thread(task1).start();
new Thread(task2).start();
}
}
5. スレッドのライフサイクル
Threadクラスにはライフサイクルがあります。代表的な状態は「新規」「実行可能」「実行中」「待機」「終了」です。プログラムの流れを理解するにはこのライフサイクルを知っておくことが大切です。
- 新規(New): インスタンスを生成した直後
- 実行可能(Runnable): startメソッドを呼び出してCPU待ちの状態
- 実行中(Running): 実際に処理を行っている状態
- 待機(Waiting / Timed Waiting): joinやsleepなどで一時停止
- 終了(Terminated): runメソッドが完了した状態
6. よく使うThreadのメソッド
Threadクラスには便利なメソッドがいくつも用意されています。例えばsleepで一時停止、joinで他のスレッドの終了待ち、currentThreadで現在実行中のスレッド情報を取得することができます。
public class ThreadMethodsExample {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
System.out.println("子スレッド開始");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子スレッド終了");
});
t.start();
t.join();
System.out.println("メインスレッド終了");
}
}
7. Threadクラスを使う上での注意点
初心者が注意すべき点は、スレッドの競合や同期です。複数のスレッドが同じ変数を同時に書き換えると予期しないバグが発生します。そのため、synchronizedを使って同期を取ることが重要です。また無限ループのような設計をするとCPUを圧迫するため、sleepを組み合わせて制御するのが一般的です。
8. java.langパッケージとThreadの位置づけ
java.langパッケージには、Javaプログラムを構築するための基本的なクラスが揃っています。StringやMath、Objectと並んでThreadは最も重要な基礎クラスの一つです。特別なインポートをしなくても使えるという点も初心者にとって学習しやすい特徴です。
まとめ
JavaのThreadクラスは、複数の処理を同時に動かすための非常に重要な仕組みです。ひとつのプログラムの中で複数の作業を並行して進めることができるため、ゲームやチャット、Webアプリケーションなどあらゆる場面で利用されています。初心者でも、Threadクラスを継承する方法やRunnableインターフェースを実装する方法を覚えることで、基本的なマルチスレッド処理を簡単に書けるようになります。
また、複数のスレッドを同時に実行する例では、実際に二つのタスクが交互に動いている様子を確認することができます。さらに、スレッドのライフサイクルや、sleep、joinなどの便利なメソッドを使うことで、スレッドの実行タイミングや終了を制御できるようになります。処理の順番をコントロールしたい場合や、安全にデータを扱いたい場合には、同期や排他の考え方も大切です。
特に、複雑な処理を同時進行させるときには、競合やバグが発生しやすくなります。synchronizedを使ったり、共有データの扱いを工夫したりすることで、安定したプログラムになります。初心者のうちは難しく感じるかもしれませんが、ThreadクラスとRunnableの基本を理解しておけば、徐々に応用もできるようになります。
実際にスレッドが処理を行う例をもう一度振り返ってみましょう。Threadを継承したクラスでも、Runnableで動かす方法でも、startメソッドを呼び出すことで新しいスレッドが動き出します。この仕組みに慣れてくると、複数の機能を同時に実行できるJavaの強力さを体感できるようになります。
スレッドの動作を確認するサンプル
public class SummaryThreadSample {
public static void main(String[] args) {
Runnable printTask = () -> {
for (int i = 1; i <= 3; i++) {
System.out.println("別スレッド処理中: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread workerThread = new Thread(printTask);
workerThread.start();
for (int i = 1; i <= 3; i++) {
System.out.println("メインスレッド処理中: " + i);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
メインスレッド処理中: 1
別スレッド処理中: 1
メインスレッド処理中: 2
別スレッド処理中: 2
メインスレッド処理中: 3
別スレッド処理中: 3
このように、メインスレッドと別のスレッドが交互に動きながら処理を進めることが分かります。Threadクラスを使うことで、待機を挟みながら柔軟に制御できるため、ゲームの描画やファイル処理、サーバー通信などでも活用できます。
生徒
「ThreadクラスとRunnableを使い分ける理由が分かってきた気がします。Runnableの方が柔軟に使えるんですね。」
先生
「そうです。特にクラスの継承ができない状況ではRunnableを使う方が便利です。そしてstartメソッドを呼び出して動かす点は同じなので、基本を押さえれば応用できます。」
生徒
「ライフサイクルの流れも理解できました。sleepやjoinなどを使うときの意味もよく分かりました。」
先生
「スレッドは便利ですが、同時に動くことで予想外の動作が起こることもあります。同期や共有データの扱いには注意しながら、安全なコードを書くことが大事です。」
生徒
「複数の処理を同時に動かせるので、ゲームやサーバー処理にも役立つと感じました。もっと複雑な動きを作ってみたいです。」
先生
「その気持ちが大事です。まずは簡単なThreadとRunnableの動作を試しながら、徐々に応用例に挑戦してみると理解が深まりますよ。」