JavaのThreadクラスを完全ガイド!初心者でもわかるスレッドの基本と使い方
生徒
「Javaで同時に複数の処理を実行したいんですけど、どうすればいいですか?」
先生
「その場合はThreadクラスを使うと便利です。Javaのjava.langパッケージに含まれていて、並行処理を実現できますよ。」
生徒
「Threadクラスって具体的にどうやって使うんですか?」
先生
「それでは、初心者向けにThreadクラスの基本から実際の使い方まで丁寧に説明していきましょう。」
1. 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は「やってほしい仕事の設計図」を用意して、Thread(実行係)に渡して動かす考え方です。クラスを継承せずに使えるので、既に別のクラスを継承している場合でも採用できます。役割を分けられるため、テストや再利用もしやすく、実務でもこちらが主流です。
手順はとてもシンプルです。① Runnableを実装してrun()の中にやりたい処理を書く ② それをThreadに渡す ③ start()で別の流れを始める――この3つだけ。※run()を直接呼ぶと単なるメソッド実行になり、別スレッドにはなりません。
class MyTask implements Runnable {
@Override
public void run() {
// ここが「別スレッド」で動く処理
System.out.println("実行中: " + Thread.currentThread().getName());
for (int i = 1; i <= 3; i++) {
System.out.println("作業ステップ " + i);
}
}
}
public class RunnableExample {
public static void main(String[] args) {
System.out.println("メイン: " + Thread.currentThread().getName());
// 仕事の中身(Runnable)を用意して、実行係(Thread)に渡す
Thread thread = new Thread(new MyTask());
thread.setName("ワーカー1"); // 分かりやすいように名前を付ける
// ここで別スレッドが動き出す(runは内部で呼ばれる)
thread.start();
// メインの流れはそのまま続く
System.out.println("メインは続行中");
}
}
メイン: main
メインは続行中
実行中: ワーカー1
作業ステップ 1
作業ステップ 2
作業ステップ 3
ポイント:Threadは「器」、Runnableは「中身」。この分担を覚えると、後の応用でも迷いにくくなります。
4. 複数スレッドの同時実行
「4. 複数スレッドの同時実行」の重要ポイントを、初心者の方にも分かりやすく簡潔に解説します。
複数のスレッドを走らせると、ひとつのプログラムの中で「Aの作業」と「Bの作業」が同じ時間帯に少しずつ進みます。大事なのは、どちらが先に動くか・どの順番で表示されるかは毎回変わる(=入れ替わる)という点です。ここでは動きが見やすいようにスレッドに名前を付け、ほんの少し休憩させながら(短い待ち時間)交互に出力してみます。
ポイントは次の3つだけです。① 仕事の中身はRunnableで用意する ② それぞれを別のThreadに渡す ③ start()を呼ぶと同時進行が始まる――この流れを押さえればOKです。
public class MultiThreadExample {
public static void main(String[] args) {
Runnable task1 = () -> {
Thread.currentThread().setName("タスク1");
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try { Thread.sleep(100); } catch (InterruptedException ignored) {}
}
};
Runnable task2 = () -> {
Thread.currentThread().setName("タスク2");
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try { Thread.sleep(100); } catch (InterruptedException ignored) {}
}
};
// 2本のスレッドを同時に開始
new Thread(task1).start();
new Thread(task2).start();
// 参考: メインの流れも同時に動いている
System.out.println("メインは同時進行中");
}
}
メインは同時進行中
タスク1 : 1
タスク2 : 1
タスク2 : 2
タスク1 : 2
タスク1 : 3
タスク2 : 3
...(順序は毎回入れ替わります)
表示順が毎回変わるのは、2本のスレッドが「交替で少しずつ進む」ためです。これが同時実行の基本イメージです。名前を付けておくと、どのスレッドの出力かが一目で分かり、動作確認がぐっと楽になります。
5. スレッドのライフサイクル
スレッドは「生まれて→走って→待って→終わる」という決まった道のり(状態の変化)を辿ります。今どの状態にいるかを知ると、動きのイメージがつかみやすく、原因調査もしやすくなります。Javaでは代表的に次の状態があります。
- 新規(NEW):
new Thread(...)した直後。まだ走っていない待機前の箱。 - 実行可能(RUNNABLE):
start()後にCPUで動く順番待ち~実行中を含む広い状態。 - 待機(WAITING):別スレッドの終了待ちなどで期限なしに一時停止(例:
join()を待つ側)。 - 時間付き待機(TIMED_WAITING):
sleep(100)のように一定時間だけ止まっている状態。 - ブロック(BLOCKED):他のスレッドが持つロックの解放待ち(
synchronizedの順番待ち)。 - 終了(TERMINATED):
run()が最後まで実行されて終わった状態。
名前が似ていて混乱しがちですが、RUNNABLE=実行中も含む「走れる状態の総称」と覚えると迷いません。実際にgetState()で状態を覗いてみましょう(表示順は環境で前後します)。
public class ThreadLifeCycleDemo {
public static void main(String[] args) throws InterruptedException {
Thread worker = new Thread(() -> {
// 少しだけ眠って「時間付き待機」に入る
try { Thread.sleep(200); } catch (InterruptedException ignored) {}
// ここで少し作業したふり
for (int i = 1; i <= 3; i++) System.out.println("作業 " + i);
});
// まだ start 前:NEW
System.out.println("1) " + worker.getState()); // NEW
worker.start(); // 実行可能(RUNNABLE)へ
// 少し待って状態を覗く(sleep 中なら TIMED_WAITING が見える)
Thread.sleep(50);
System.out.println("2) " + worker.getState()); // RUNNABLE or TIMED_WAITING
// worker の終了を待つ(待っているのは「メイン」側)
worker.join();
System.out.println("3) " + worker.getState()); // TERMINATED
}
}
1) NEW
2) TIMED_WAITING (または RUNNABLE)
作業 1
作業 2
作業 3
3) TERMINATED
読み取りのコツ:NEW → RUNNABLE ↔(WAITING/TIMED_WAITING/BLOCKED)→ RUNNABLE → TERMINATEDと行き来します。多くの入門バグは「どの状態で止まっているのか」が分かると解決に近づきます。まずはgetState()で現在地を確認する習慣を付けましょう。
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クラスを使う上での注意点
「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の動作を試しながら、徐々に応用例に挑戦してみると理解が深まりますよ。」
この記事を読んだ人からの質問
「この記事を読んだ人からの質問」の重要ポイントを、初心者の方にも分かりやすく簡潔に解説します。