
Java超|スレッドを作る2つの方法
スレッドの作り方は1つだけではない。Thread を受け継ぐ道と、Runnable で任務内容を渡す道を知ると、Javaのクラス設計はもっと柔軟になります。
これまでのスレッド学習では、Thread クラスを継承して run メソッドを定義し、start メソッドで起動する方法を見てきました。
この方法は、とても分かりやすいです。
Thread を継承したクラスは、スレッドとして動くための機能を持ちます。
その中の run メソッドに、別スレッドで実行したい処理を書きます。
そして、作成したオブジェクトに start を呼び出すと、新しい処理の流れが始まります。
たとえば、ドラゴンボール風にたとえるなら、戦士クラスそのものが「別任務へ出られる戦士」として作られているようなものです。
class WarriorTask extends Thread
{
public void run()
{
別スレッドで行いたい処理
}
}この形では、WarriorTask 自身が Thread の力を受け継いでいます。
そのため、WarriorTask のオブジェクトに対して start を呼び出せます。
しかし、Javaではいつもこの方法だけで十分とは限りません。
なぜなら、Javaのクラスは 1つのクラスしか継承できない からです。
たとえば、あるクラスがすでに別の親クラスを継承している場合、さらに Thread クラスを継承することはできません。
ドラゴンボール風に言えば、ある戦士がすでに「記録係の家系」や「支援部隊の流派」を受け継いでいる場合、さらに Thread という別の親クラスまで同時に受け継ぐことはできない、というイメージです。
けれども、別任務で動く方法は1つだけではありません。
Thread を親クラスとして受け継がなくても、「別スレッドで動く任務内容」を持たせる方法があります。
それが Runnable インターフェイスを実装する方法 です。
Javaでスレッドを作る主な方法は、次の2つです。
| 方法 | 基本形 | 特徴 |
|---|---|---|
| Thread クラスを継承する | extends Thread | スレッド用クラスを直接作るので分かりやすい |
| Runnable インターフェイスを実装する | implements Runnable | ほかのクラス継承と両立しやすい |
この記事では、なぜスレッドの作り方が2つあるのか、Runnable を使うと何が変わるのか、Sample6.java を使って、ドラゴンボールの世界観でやわらかく整理していきます。
これまでの方法は Thread クラスを継承するやり方
まず、これまで使ってきた Thread 継承の形を整理しておきましょう。
Thread クラスを継承する場合、基本形は次のようになります。
class クラス名 extends Thread
{
public void run()
{
別スレッドで行いたい処理
}
}この形では、クラスそのものが Thread の機能を受け継ぎます。
そのため、作成したオブジェクトに対して start を呼び出せます。
WarriorTask task1 = new WarriorTask();
task1.start();この書き方は、スレッドの基本を学ぶ段階ではとても理解しやすいです。
| 要素 | 役割 |
|---|---|
| extends Thread | スレッドとして動けるクラスにする |
| run | 別スレッドで行う処理を書く |
| start | 新しいスレッドを起動する |
ドラゴンボール風に言えば、WarriorTask は「最初から別任務へ出られる戦士クラス」です。
戦士オブジェクトを作り、start で出撃命令を出せば、run に書いた任務内容が別の流れで動き始めます。
Thread 継承の分かりやすさ
Thread 継承のよいところは、流れがまっすぐなことです。
| 手順 | 内容 |
|---|---|
| 1 | Thread を継承したクラスを作る |
| 2 | run に処理を書く |
| 3 | オブジェクトを作る |
| 4 | start を呼ぶ |
| 5 | run が別スレッドで動く |
クラス自身がスレッドなので、考え方がシンプルです。
class WarriorTask extends Thread
{
public void run()
{
System.out.println("戦士が任務を進めています。");
}
}このように書けば、WarriorTask のオブジェクトは start を使って起動できます。
ドラゴンボール風に言うと、WarriorTask は「任務に出る力」を最初から持った戦士です。
だから、任務内容を書いた run を持たせて、start で動かせばよいわけです。
でも Thread クラスを継承できない場面がある
ここで問題になるのが、Javaの継承ルールです。
Javaでは、1つのクラスが同時に複数のクラスを継承することはできません。
| 状況 | Javaでできるか |
|---|---|
| 1つのクラスだけを継承する | できる |
| 2つ以上のクラスを同時に継承する | できない |
たとえば、次のようなクラスがあるとします。
class TrainingRecordBase
{
}そして、修行記録を扱う戦士クラスを TrainingRecordBase から継承して作りたいとします。
class TrainingRecorder extends TrainingRecordBase
{
}この TrainingRecorder は、すでに TrainingRecordBase を継承しています。
この状態で、さらに Thread も継承することはできません。
class TrainingRecorder extends TrainingRecordBase, Thread
{
}このような複数クラスの同時継承は、Javaではできません。
ドラゴンボール風にたとえると、GohanAnalyzer がすでに「記録解析部隊の親クラス」を受け継いでいる場合、さらに Thread という親クラスまで同時に受け継ぐことはできない、という感じです。
けれども、GohanAnalyzer が別任務で動くための「任務内容」を身につけることはできます。
そこで Runnable が登場します。
Runnable インターフェイスを使う方法
Runnable は、別スレッドで動かしたい処理内容を表すためのインターフェイスです。
Runnable を実装するクラスでは、run メソッドを定義します。
基本形は次のようになります。
class クラス名 implements Runnable
{
public void run()
{
別スレッドで行いたい処理
}
}Thread 継承と似ていますが、宣言が違います。
| 方法 | クラス宣言 |
|---|---|
| Thread 継承 | class クラス名 extends Thread |
| Runnable 実装 | class クラス名 implements Runnable |
Runnable を実装したクラスは、Thread クラスを継承していません。
そのため、別のクラスを継承しながら、Runnable を実装するという設計がしやすくなります。
ドラゴンボール風に言えば、Thread 継承は「戦士クラスそのものが別任務用の力を親クラスから受け継ぐ」方法です。
一方、Runnable 実装は「戦士が別任務で行う任務内容を持つ」方法です。
Thread を親クラスとして受け継がなくても、Runnable という約束を守り、run に任務内容を書けば、あとで Thread オブジェクトに渡して動かせます。
図:Thread 継承と Runnable 実装の2つの道

この図が示していること
この図は、Javaでスレッドを作る2つの方法を比較しています。
左側は Thread クラスを継承する方法です。
クラス自身が Thread の機能を持つため、そのオブジェクトに start を呼び出せます。
右側は Runnable インターフェイスを実装する方法です。
Runnable 実装クラスは、別スレッドで動かす処理内容を持ちます。
そのオブジェクトを Thread オブジェクトに渡し、Thread 側に start を呼び出して起動します。
| 図の要素 | 意味 |
|---|---|
| extends Thread | Thread クラスを継承してスレッド用クラスを作る |
| implements Runnable | Runnable を実装して処理内容を作る |
| run | どちらの方法でも必要な処理本体 |
| start | 新しいスレッドを起動する |
| 多重継承できない | Runnable が必要になる大きな理由 |
この図から分かることは、スレッドを作る方法は1つではなく、クラス設計に合わせて選べるということです。
Runnable でも run メソッドを書く
Runnable を使う場合でも、run メソッドを書く点は変わりません。
Thread 継承でも Runnable 実装でも、別スレッドで行いたい処理は run に書きます。
| 方法 | run は必要か |
|---|---|
| Thread 継承 | 必要 |
| Runnable 実装 | 必要 |
違うのは、start をどこに呼ぶかです。
Thread 継承では、そのオブジェクト自身が Thread なので、自分自身に start を呼べます。
WarriorTask task1 = new WarriorTask("GokuWarrior");
task1.start();Runnable 実装では、Runnable オブジェクト自身には start はありません。
そのため、Thread オブジェクトを別に作る必要があります。
WarriorTask task1 = new WarriorTask("GokuWarrior");
Thread th1 = new Thread(task1);
th1.start();この違いを押さえることが、Runnable 方式の理解ではとても大切です。
Runnable 実装クラスは処理内容を持つ
Runnable を実装したクラスのオブジェクトは、「別スレッドで何をするか」を持っています。
しかし、そのオブジェクト自身がスレッドとして起動するわけではありません。
| オブジェクト | 役割 |
|---|---|
| Runnable 実装オブジェクト | 別スレッドで行う処理内容を持つ |
| Thread オブジェクト | 実際に新しいスレッドを起動する |
ドラゴンボール風にたとえると、Runnable 実装オブジェクトは「任務内容が書かれた指令札」を持つ戦士です。
しかし、その指令札だけでは任務は始まりません。
その任務内容を Thread という出撃役に渡し、Thread に start を呼び出すことで、実際に任務が始まります。
Runnable 方式の起動手順
Runnable 方式では、次の3段階でスレッドを起動します。
| 手順 | 内容 |
|---|---|
| 1 | Runnable を実装したクラスのオブジェクトを作る |
| 2 | そのオブジェクトを引数にして Thread オブジェクトを作る |
| 3 | Thread オブジェクトに start を呼ぶ |
コードで見ると、次のようになります。
WarriorTask task1 = new WarriorTask("GokuWarrior");
Thread th1 = new Thread(task1);
th1.start();この3行には、それぞれ役割があります。
| コード | 役割 |
|---|---|
| new WarriorTask("GokuWarrior") | 別スレッドで動かす処理内容を持つオブジェクトを作る |
| new Thread(task1) | task1 の run を動かすための Thread オブジェクトを作る |
| th1.start() | 新しいスレッドを起動する |
ここで start を呼ぶ相手は task1 ではありません。
start を呼ぶのは th1 です。
th1.start();Runnable 方式では、この点を間違えないようにすることが大切です。
図:Runnable 方式でスレッドを起動する流れ

この図が示していること
この図は、Runnable 方式でスレッドを起動する流れを表しています。
Runnable を実装した WarriorTask は、run に処理内容を持ちます。
そのオブジェクト task1 を作っただけでは、まだスレッドは始まりません。
task1 を Thread オブジェクトに渡し、th1.start() を呼ぶことで、task1 の run が別スレッドで動き始めます。
| 図の要素 | 意味 |
|---|---|
| implements Runnable | 別スレッドで動かす処理内容を持つ |
| task1 | Runnable 実装オブジェクト |
| new Thread(task1) | task1 を動かす Thread オブジェクトを作る |
| th1.start() | 新しいスレッドを起動する |
| task1 の run | 別スレッドで実行される処理 |
この図から分かることは、Runnable 方式では「処理内容」と「スレッドとして起動する役」が分かれているということです。
Runnable 実装を確認する
ここでは、Runnable インターフェイスを実装する形でスレッドを作ります。
GokuWarrior は、別スレッドで修行手順を確認します。
main 側では、VegetaWarrior が作戦の流れを整理します。
ファイル名:Sample6.java
class WarriorTask implements Runnable
{
private String name;
public WarriorTask(String nm)
{
name = nm;
}
public void run()
{
// 別スレッドで進める修行の流れ
for(int i = 0; i < 5; i++) {
System.out.println(name + "が修行の手順を確認しています。");
}
}
}
class Sample6
{
public static void main(String[] args)
{
WarriorTask task1 = new WarriorTask("GokuWarrior");
Thread th1 = new Thread(task1);
th1.start();
for(int i = 0; i < 5; i++) {
System.out.println("VegetaWarriorが作戦の流れを整理しています。");
}
}
}Sample6.java の全体像
このプログラムでは、WarriorTask クラスが Runnable を実装しています。
class WarriorTask implements Runnableこの宣言によって、WarriorTask は Runnable の約束に従うクラスになります。
Runnable を実装するクラスでは、run メソッドを定義します。
public void run()
{
for(int i = 0; i < 5; i++) {
System.out.println(name + "が修行の手順を確認しています。");
}
}この run に、別スレッドで行いたい処理を書いています。
ここでは、GokuWarrior が修行の手順を確認しているメッセージを5回表示します。
WarriorTask は Thread を継承していない
重要なのは、WarriorTask が Thread を継承していないことです。
class WarriorTask implements Runnableこのクラスは、extends Thread ではありません。
そのため、WarriorTask のオブジェクト自身に start を呼ぶことはできません。
WarriorTask task1 = new WarriorTask("GokuWarrior");この task1 は、別スレッドで動かす処理内容を持っています。
しかし、task1 自身が Thread オブジェクトではありません。
| オブジェクト | 内容 |
|---|---|
| task1 | Runnable 実装オブジェクト |
| 持っているもの | run に書かれた処理内容 |
| 持っていないもの | start で起動する機能 |
そのため、次に Thread オブジェクトを作ります。
Thread th1 = new Thread(task1);このコードにより、task1 の run を別スレッドで動かす準備ができます。
Thread オブジェクトを作って start する
Runnable 方式では、Thread オブジェクトを別に作ります。
Thread th1 = new Thread(task1);ここで task1 を Thread のコンストラクタに渡しています。
これは、
この task1 の run を、新しいスレッドで動かす準備をしてください
という意味です。
そして、start を呼び出します。
th1.start();ここで新しいスレッドが起動します。
このとき動くのは、task1 の run です。
| コード | 意味 |
|---|---|
| WarriorTask task1 = new WarriorTask("GokuWarrior"); | GokuWarrior の任務内容を持つオブジェクトを作る |
| Thread th1 = new Thread(task1); | task1 を動かすための Thread を作る |
| th1.start(); | 新しいスレッドを起動する |
| task1 の run | 別スレッドで実行される |
ドラゴンボール風に言えば、GokuWarrior は「修行手順を確認する」という任務内容を持っています。
ただし、その任務札だけでは出撃できません。
Thread という出撃役に GokuWarrior の任務内容を渡し、th1.start() で任務開始の合図を出すことで、GokuWarrior の run が別の流れで動き出します。
main 側の処理も進む
th1.start() を呼び出したあと、main 側も自分の処理を続けます。
for(int i = 0; i < 5; i++) {
System.out.println("VegetaWarriorが作戦の流れを整理しています。");
}このため、プログラムには2つの流れができます。
| 流れ | 内容 |
|---|---|
| th1 のスレッド | GokuWarrior が修行の手順を確認する |
| main スレッド | VegetaWarrior が作戦の流れを整理する |
この2つは並行して進むため、表示順は固定されません。
実行すると、たとえば次のように表示されることがあります。
GokuWarriorが修行の手順を確認しています。
VegetaWarriorが作戦の流れを整理しています。
GokuWarriorが修行の手順を確認しています。
VegetaWarriorが作戦の流れを整理しています。
VegetaWarriorが作戦の流れを整理しています。
GokuWarriorが修行の手順を確認しています。別の実行では、VegetaWarrior の表示が先にまとまって出ることもあります。
GokuWarrior の表示が先に進むこともあります。
これは、スレッドの動きとして自然です。
作り方が Runnable 方式になっても、start をきっかけに新しい処理の流れが生まれる点は、Thread 継承と同じです。
Thread 継承と Runnable 実装の違い
ここで、2つの方法を比較して整理します。
| 項目 | Thread 継承 | Runnable 実装 |
|---|---|---|
| クラス宣言 | extends Thread | implements Runnable |
| run の定義 | 必要 | 必要 |
| start の呼び出し先 | 作成したオブジェクト自身 | Thread オブジェクト |
| Thread オブジェクト作成 | 自分自身が Thread | 別に new Thread(task) が必要 |
| 分かりやすさ | シンプル | 手順が少し増える |
| 柔軟性 | ほかのクラス継承と両立しにくい | ほかのクラス継承と両立しやすい |
Thread 継承では、クラスそのものがスレッドです。
Runnable 実装では、クラスは「スレッドで行う処理内容」を持ち、実際の起動は Thread オブジェクトに任せます。
この違いが、設計上とても大切です。
Runnable の方法が柔軟な理由
Runnable 方式が柔軟なのは、役割が分かれているからです。
| 役割 | Runnable 方式での担当 |
|---|---|
| 何をするか | Runnable 実装クラス |
| どう起動するか | Thread オブジェクト |
Runnable 実装クラスは、run の中に処理内容を書きます。
Thread オブジェクトは、その処理内容を新しいスレッドとして動かします。
つまり、Runnable 方式では、
処理内容と起動役を分ける
という設計になります。
ドラゴンボール風に言えば、GokuWarrior は「修行手順を確認する」という任務内容を持っています。
一方、Thread は、その任務内容を実際に別任務として動かす出撃役です。
任務内容と出撃役が分かれているので、クラス設計が柔軟になります。
すでに別クラスを継承している場合にも使いやすい
Runnable の大きな利点は、すでに別のクラスを継承している場合でも使いやすいことです。
Javaではクラスを2つ同時に継承できません。
しかし、インターフェイスは実装できます。
たとえば、次のような考え方ができます。
class TrainingRecorder extends RecordBase implements Runnable
{
public void run()
{
修行記録を整理する処理
}
}このように、RecordBase を継承しながら、Runnable を実装できます。
| 要素 | 意味 |
|---|---|
| extends RecordBase | 記録係としての性質を受け継ぐ |
| implements Runnable | 別スレッドで動かす処理内容を持つ |
| run | 別スレッドで行う処理 |
ドラゴンボール風に言えば、GohanAnalyzer が「記録解析部隊の性質」を受け継ぎながら、「別任務で動くための任務内容」も持てるということです。
Thread クラスそのものを継承できなくても、Runnable を使えば、スレッドで動かす処理を組み込めます。
図:Runnable は処理内容と起動役を分ける

この図が示していること
この図は、Runnable 方式の役割分担を表しています。
Runnable 実装クラスは、run に処理内容を書きます。
Thread オブジェクトは、その Runnable 実装オブジェクトを受け取り、start によって新しいスレッドを起動します。
| 図の要素 | 意味 |
|---|---|
| Runnable 実装クラス | 何をするかを持つ |
| Thread オブジェクト | どう起動するかを担当する |
| new Thread(task) | 処理内容を Thread に渡す |
| th.start() | 新しいスレッドを起動する |
| task の run | 新しい流れで実行される |
この図から分かることは、Runnable 方式では、クラスが直接 Thread になるのではなく、処理内容を Thread に渡して動かすということです。
どちらの方法を使えばよいのか
学習の最初では、Thread 継承の方法が分かりやすいです。
クラスそのものがスレッドになるので、start までの流れが直感的です。
ただし、実際のクラス設計では、Runnable 方式のほうが使いやすい場面も多くあります。
| 場面 | 向いている方法 |
|---|---|
| スレッドの基本を理解したい | Thread 継承 |
| すでに別のクラスを継承している | Runnable 実装 |
| 処理内容と起動役を分けたい | Runnable 実装 |
| 設計を柔軟にしたい | Runnable 実装 |
つまり、Thread 継承は「基本をつかみやすい方法」です。
Runnable 実装は「設計を柔軟にしやすい方法」です。
どちらが完全に正しい、という話ではありません。
状況に応じて選べることが大切です。
Runnable 方式で間違えやすいところ
Runnable 方式では、次の点に注意が必要です。
| 間違えやすい点 | 正しい理解 |
|---|---|
| Runnable 実装オブジェクトに start を呼ぶ | start は Thread オブジェクトに呼ぶ |
| task1 を作っただけでスレッドが動く | task1 を Thread に渡し、start が必要 |
| Runnable では run が不要 | Runnable でも run を定義する |
| Thread 継承とまったく違う動きになる | どちらも最終的には別スレッドで run が動く |
特に大切なのは、start の呼び出し先です。
Runnable 方式では、次のように書きます。
Thread th1 = new Thread(task1);
th1.start();task1.start() ではありません。
なぜなら、task1 は Thread オブジェクトではなく、Runnable 実装オブジェクトだからです。
ドラゴンボール風に言えば、任務内容を書いた指令札そのものに「出撃せよ」と言っても動きません。
その指令札を出撃役である Thread に渡し、Thread に対して start を命じる必要があります。
Thread 継承と Runnable 実装の共通点
2つの方法には違いがありますが、共通点もあります。
| 共通点 | 内容 |
|---|---|
| run を定義する | 別スレッドで行う処理を書く |
| start で起動する | 新しい処理の流れを作る |
| main と並行して動く | main の処理も続く |
| 表示順は固定されない | 複数の流れが動くため順番は変わる |
作り方が違っても、スレッドとして動く本質は同じです。
新しいスレッドが起動し、run の中の処理が main とは別の流れで実行されます。
Sample6.java でも、GokuWarrior の run と、main 側の VegetaWarrior の処理が並行して進みます。
この点は、Thread 継承で作った場合と同じです。
ドラゴンボール風に整理する
今回の内容をドラゴンボール風に整理すると、次のようになります。
| Java | ドラゴンボール風のイメージ |
|---|---|
| Thread 継承 | 戦士クラスそのものが別任務に出られる力を受け継ぐ |
| Runnable 実装 | 戦士が別任務で行う任務内容を持つ |
| run | 任務内容を書いた作戦書 |
| Thread オブジェクト | 実際に出撃させる役 |
| new Thread(task1) | 任務内容を出撃役へ渡す |
| start | 出撃命令 |
| Javaの単一継承 | 親クラスとして受け継げるクラスは1つだけ |
| Runnable の柔軟性 | ほかの性質を受け継ぎながら別任務内容も持てる |
Thread 継承は、スレッドの基本をつかみやすい方法です。
Runnable 実装は、クラス設計を柔軟にしやすい方法です。
どちらの方法でも、run に別スレッドで行いたい処理を書き、start によって新しい処理の流れを起動します。
ただし、Runnable 方式では、Runnable 実装オブジェクト自身に start を呼ぶのではなく、Thread オブジェクトを作って、その Thread に start を呼ぶ点が重要です。
スレッドを作る2つの方法で押さえておきたいこと
スレッドを作る2つの方法を学ぶときは、次の点を押さえると理解しやすくなります。
| ポイント | 内容 |
|---|---|
| スレッド作成方法は2つある | Thread 継承と Runnable 実装 |
| Thread 継承 | クラス自身がスレッドとして動ける |
| Runnable 実装 | 別スレッドで動かす処理内容を持つ |
| Javaは多重継承できない | 複数のクラスを同時に継承できない |
| Runnable は柔軟 | ほかのクラス継承と組み合わせやすい |
| どちらも run が必要 | 別スレッドで行う処理を書く場所 |
| Runnable 方式では Thread が必要 | new Thread(task1) の形で起動役を作る |
| start は Thread に呼ぶ | Runnable 実装オブジェクト自身には start しない |
| 動きの本質は同じ | どちらも新しい処理の流れを作る |
スレッドを作る方法は1つではありません。
Thread クラスを継承する方法は、分かりやすくスレッドの基本をつかみやすい方法です。
Runnable インターフェイスを実装する方法は、処理内容と起動役を分けられるため、クラス設計を柔軟にしやすい方法です。
この2つの違いが分かると、スレッドは単なる書き方の暗記ではなく、クラス設計と処理の流れをどう組み立てるかという視点で理解できるようになります。
