
Java超|Threadクラスで複数処理を動かす
仲間の数だけ、処理の流れも動き出す。Threadクラスを使うと、Javaプログラムの中で複数の処理を並行して進められるようになります。
前回は、main メソッドの流れとは別に、新しいスレッドを1つ起動する方法を学びました。
main の流れが1本あり、そこに start メソッドで新しいスレッドを1本追加することで、処理の流れが2本になりました。
今回は、そこからさらに一歩進みます。
スレッドは、1つだけしか起動できないわけではありません。
Thread クラスを継承したクラスのオブジェクトを複数作り、それぞれに start メソッドを呼び出せば、複数のスレッドを動かせます。
ドラゴンボール風にたとえると、戦士本部で任務が始まったとき、1人の戦士だけが動くのではなく、複数の戦士がそれぞれ別の役割で動き出すようなものです。
たとえば、次のような任務を考えてみましょう。
| 流れ | ドラゴンボール風の役割 | Javaでの意味 |
|---|---|---|
| main の流れ | 本部側で修行場を準備する支援役 | 最初から動いている main スレッド |
| 1つ目のスレッド | GokuWarrior が重力修行を進める | warrior1 のスレッド |
| 2つ目のスレッド | VegetaWarrior が戦闘フォームを整える | warrior2 のスレッド |
このように、main の流れに加えて、別のスレッドを2つ起動すると、プログラムの中には少なくとも3本の処理の流れが存在することになります。
今回のポイントは、start を呼んだ回数だけ、新しい処理の流れが増える ということです。
もちろん、何でも無制限に増やせばよいわけではありません。
スレッドが増えると、処理の順番やタイミングを考える必要も出てきます。
まずは、複数のスレッドを起動すると、main 以外にも複数の run メソッドの流れが動き出す、という基本の感覚をしっかり押さえていきましょう。
スレッドをもう1つ増やすとどうなるのか
1つのスレッドを起動したときは、処理の流れは次の2本でした。
| 流れ | 内容 |
|---|---|
| main のスレッド | 最初から動いている処理 |
| 新しく起動したスレッド | start によって増えた処理 |
では、さらにもう1つスレッドを起動するとどうなるでしょうか。
答えはシンプルです。
新しい処理の流れが、さらに1本増えます。
つまり、次のようになります。
| 流れ | 内容 |
|---|---|
| main のスレッド | 本部側の処理を進める |
| 1つ目のスレッド | 1人目の戦士が別任務を進める |
| 2つ目のスレッド | 2人目の戦士が別任務を進める |
ドラゴンボール風に言えば、本部の支援役が修行場の準備をしているあいだに、GokuWarrior が重力修行を進め、VegetaWarrior が戦闘フォームを整えるような状態です。
3人が同じ世界の中で動いていますが、1人ずつ順番待ちしているわけではありません。
それぞれが自分の流れで動いています。
複数スレッドは複数の任務の流れ
スレッドを増やすということは、プログラムの中で任務担当を増やすようなものです。
1本の処理だけなら、作業は順番に進みます。
main の流れ
↓
修行場を準備
↓
重力修行
↓
戦闘フォーム確認一方、複数スレッドを使うと、次のように複数の流れを持てます。
main の流れ :修行場を準備
warrior1 の流れ :GokuWarrior が重力修行
warrior2 の流れ :VegetaWarrior が戦闘フォーム確認このように、スレッドを増やすと、1本の一本道ではなく、複数の道が同時に進んでいるようなプログラムになります。
| 見方 | 内容 |
|---|---|
| 1本の処理 | 1人が順番に作業する |
| 複数スレッド | 複数人がそれぞれの作業を進める |
| start を1回呼ぶ | 新しい流れが1本増える |
| start を2回呼ぶ | 新しい流れが2本増える |
ここで大切なのは、main も止まらずに動き続けるということです。
start を呼んだからといって、main の処理が消えるわけではありません。
main は main として進み、新しく起動されたスレッドも、それぞれの run を進めます。
図:main に2本のスレッドを追加する流れ

この図が示していること
この図は、main の流れから2回 start を呼び出すことで、2本の新しいスレッドが生まれることを表しています。
main は最初から存在する処理の流れです。
そこから warrior1.start(); を呼ぶと、GokuWarrior のスレッドが動き出します。
さらに warrior2.start(); を呼ぶと、VegetaWarrior のスレッドも動き出します。
| 図の要素 | 意味 |
|---|---|
| mainスレッド | 最初から動いている処理の流れ |
| warrior1.start(); | 1つ目の新しいスレッドを起動する |
| warrior2.start(); | 2つ目の新しいスレッドを起動する |
| warrior1スレッド | 1つ目の run の流れ |
| warrior2スレッド | 2つ目の run の流れ |
この図から分かることは、スレッドは1本だけ増やすものではなく、必要に応じて複数起動できるということです。
2つのスレッドを起動する基本の考え方
複数のスレッドを起動するといっても、考え方は難しくありません。
1つのスレッドを起動するときと同じ手順を、複数回行います。
基本は次の流れです。
| 手順 | 内容 |
|---|---|
| 1 | Thread を継承したクラスを用意する |
| 2 | そのクラスのオブジェクトを作る |
| 3 | start を呼び出す |
| 4 | 必要なら別のオブジェクトを作る |
| 5 | そのオブジェクトにも start を呼び出す |
つまり、2つのスレッドを起動したいなら、スレッド用のオブジェクトを2つ作り、それぞれに start を呼びます。
WarriorWorker warrior1 = new WarriorWorker("GokuWarrior");
warrior1.start();
WarriorWorker warrior2 = new WarriorWorker("VegetaWarrior");
warrior2.start();これで、warrior1 と warrior2 は、それぞれ別のスレッドとして動き始めます。
同じクラスから複数のスレッドを作れる
ここで重要なのは、同じクラスから作ったオブジェクトでも、それぞれ別のスレッドとして動ける という点です。
たとえば、WarriorWorker という1つのクラスを作っておけば、そこから複数の戦士オブジェクトを作れます。
| オブジェクト | 名前 | 動き |
|---|---|---|
| warrior1 | GokuWarrior | GokuWarrior 用のスレッドとして動く |
| warrior2 | VegetaWarrior | VegetaWarrior 用のスレッドとして動く |
これは、オブジェクト指向の考え方としても大切です。
クラスは設計図です。
オブジェクトは、その設計図から作られた実体です。
ドラゴンボール風にたとえると、WarriorWorker クラスは「別任務で修行できる戦士の設計図」です。
そこから作られた warrior1 は GokuWarrior という戦士。
warrior2 は VegetaWarrior という戦士です。
同じ設計図から生まれていても、2人は別々の存在です。
それぞれに start を呼べば、それぞれが別の任務の流れを持って動き出します。
Threadクラスを継承したクラスの役割
複数スレッドでも、基本は Thread クラスの継承です。
class WarriorWorker extends Thread
{
private String name;
public WarriorWorker(String nm)
{
name = nm;
}
public void run()
{
for(int i = 0; i < 5; i++){
System.out.println(name + "が修行メニューを進めています。");
}
}
}この WarriorWorker クラスは、Thread を継承しています。
そのため、WarriorWorker のオブジェクトは start で起動できます。
| 部分 | 役割 |
|---|---|
| extends Thread | スレッドとして動けるクラスにする |
| name | 戦士の名前を保存する |
| コンストラクタ | オブジェクト作成時に名前を受け取る |
| run | 別スレッドで実行する修行内容を書く |
このクラスを1つ用意しておけば、GokuWarrior 用、VegetaWarrior 用、さらに別の戦士用のオブジェクトも作れます。
runメソッドは各スレッドの任務内容
run メソッドには、そのスレッドで実行したい処理を書きます。
public void run()
{
for(int i = 0; i < 5; i++){
System.out.println(name + "が修行メニューを進めています。");
}
}この run は、WarriorWorker オブジェクトごとに実行されます。
warrior1 のスレッドでは、name が GokuWarrior です。
warrior2 のスレッドでは、name が VegetaWarrior です。
そのため、同じ run のコードを使っていても、表示される名前が変わります。
| スレッド | name の値 | 表示される内容 |
|---|---|---|
| warrior1 | GokuWarrior | GokuWarriorが修行メニューを進めています。 |
| warrior2 | VegetaWarrior | VegetaWarriorが修行メニューを進めています。 |
ドラゴンボール風に言えば、同じ修行メニューを持つ戦士クラスでも、GokuWarrior と VegetaWarrior はそれぞれ別の戦士です。
同じ作戦書を持っていても、それぞれの名前で任務報告を出します。
複数スレッドを確認する
ここでは、Thread クラスを継承した WarriorWorker クラスを使い、main 以外に2本のスレッドを起動します。
main 側では本部の支援役が修行場の準備を進めます。
その一方で、GokuWarrior と VegetaWarrior がそれぞれ別スレッドとして修行メニューを進めます。
ファイル名:Sample2.java
class WarriorWorker extends Thread
{
private String name;
public WarriorWorker(String nm)
{
name = nm;
}
public void run()
{
// それぞれの戦士が別スレッドで修行する
for(int i = 0; i < 5; i++){
System.out.println(name + "が修行メニューを進めています。");
}
}
}
class Sample2
{
public static void main(String[] args)
{
WarriorWorker warrior1 = new WarriorWorker("GokuWarrior");
warrior1.start();
WarriorWorker warrior2 = new WarriorWorker("VegetaWarrior");
warrior2.start();
// main側の流れ
for(int i = 0; i < 5; i++){
System.out.println("支援役が修行場の準備をしています。");
}
}
}プログラムの全体像
このプログラムには、2つのクラスがあります。
| クラス | 役割 |
|---|---|
| WarriorWorker | Thread を継承したスレッド用クラス |
| Sample2 | main メソッドを持つ実行用クラス |
WarriorWorker は、スレッドとして動く戦士を表すクラスです。
Sample2 は、その戦士オブジェクトを作り、start で起動するクラスです。
今回のプログラムでは、次の3本の流れが動きます。
| 流れ | 実行内容 |
|---|---|
| main のスレッド | 支援役が修行場の準備をしています。 |
| warrior1 のスレッド | GokuWarriorが修行メニューを進めています。 |
| warrior2 のスレッド | VegetaWarriorが修行メニューを進めています。 |
この3本の流れが、それぞれ独立して進みます。
1つ目のスレッドを作って起動する
main メソッドでは、まず1つ目のスレッド用オブジェクトを作っています。
WarriorWorker warrior1 = new WarriorWorker("GokuWarrior");ここでは、GokuWarrior という名前を持つ WarriorWorker オブジェクトを作っています。
この時点では、まだ新しいスレッドは動いていません。
あくまで、スレッドとして動けるオブジェクトを作っただけです。
次に、start を呼び出します。
warrior1.start();この行によって、warrior1 のスレッドが起動します。
そして、WarriorWorker クラスの run メソッドが、新しい処理の流れとして実行されます。
| コード | 意味 |
|---|---|
| new WarriorWorker("GokuWarrior") | GokuWarrior 用のオブジェクトを作る |
| warrior1.start() | GokuWarrior のスレッドを起動する |
| run | GokuWarrior が修行メニューを進める |
2つ目のスレッドを作って起動する
次に、2つ目のスレッド用オブジェクトを作っています。
WarriorWorker warrior2 = new WarriorWorker("VegetaWarrior");ここでは、VegetaWarrior という名前を持つ WarriorWorker オブジェクトを作っています。
そして、こちらにも start を呼び出します。
warrior2.start();これで、warrior2 のスレッドも起動します。
つまり、Sample2.java では、start を2回呼び出しています。
| start の呼び出し | 起動するスレッド |
|---|---|
| warrior1.start() | GokuWarrior のスレッド |
| warrior2.start() | VegetaWarrior のスレッド |
start を2回呼んでいるので、新しいスレッドも2本生まれます。
もともとの main の流れもあるため、全体では3本の流れになります。
main側も処理を続ける
2つのスレッドを起動したあと、main 側も自分の処理を続けます。
for(int i = 0; i < 5; i++){
System.out.println("支援役が修行場の準備をしています。");
}ここでは、支援役が修行場を準備しているメッセージを5回表示しています。
大切なのは、main が warrior1 や warrior2 の終了を必ず待ってから進むわけではないということです。
main は main として進みます。
warrior1 は warrior1 として進みます。
warrior2 は warrior2 として進みます。
ドラゴンボール風に言えば、本部の支援役が修行場を整えているあいだに、GokuWarrior と VegetaWarrior がそれぞれ別の修行を進めている状態です。
図:同じクラスから2つのスレッドを作る流れ

この図が示していること
この図は、WarriorWorker という同じクラスから、warrior1 と warrior2 という2つのオブジェクトを作り、それぞれを別スレッドとして起動する流れを表しています。
WarriorWorker は共通の設計図です。
そこから作られた warrior1 は GokuWarrior として動きます。
warrior2 は VegetaWarrior として動きます。
| 図の要素 | 意味 |
|---|---|
| WarriorWorker | Thread を継承した共通のクラス |
| warrior1 | GokuWarrior 用のオブジェクト |
| warrior2 | VegetaWarrior 用のオブジェクト |
| warrior1.start() | GokuWarrior のスレッドを起動する |
| warrior2.start() | VegetaWarrior のスレッドを起動する |
この図から分かることは、クラスは1つでも、オブジェクトを複数作れば、それぞれを独立したスレッドとして動かせるということです。
実行結果の見え方
Sample2.java を実行すると、次のような結果になることがあります。
支援役が修行場の準備をしています。
GokuWarriorが修行メニューを進めています。
VegetaWarriorが修行メニューを進めています。
支援役が修行場の準備をしています。
GokuWarriorが修行メニューを進めています。
支援役が修行場の準備をしています。
VegetaWarriorが修行メニューを進めています。
GokuWarriorが修行メニューを進めています。
VegetaWarriorが修行メニューを進めています。
支援役が修行場の準備をしています。
GokuWarriorが修行メニューを進めています。
VegetaWarriorが修行メニューを進めています。
支援役が修行場の準備をしています。
GokuWarriorが修行メニューを進めています。
VegetaWarriorが修行メニューを進めています。ただし、この順番は毎回同じになるとは限りません。
別の実行では、次のように表示されるかもしれません。
GokuWarriorが修行メニューを進めています。
GokuWarriorが修行メニューを進めています。
支援役が修行場の準備をしています。
VegetaWarriorが修行メニューを進めています。
支援役が修行場の準備をしています。
VegetaWarriorが修行メニューを進めています。
GokuWarriorが修行メニューを進めています。
支援役が修行場の準備をしています。
VegetaWarriorが修行メニューを進めています。このように、表示の順番は固定ではありません。
GokuWarrior が先に多く表示されることもあります。
VegetaWarrior が途中で続けて表示されることもあります。
main 側の支援役の表示が先に進むこともあります。
なぜ表示順が固定されないのか
表示順が固定されない理由は、複数のスレッドがそれぞれ独立して動いているからです。
Sample2.java では、次の3本の流れがあります。
main の流れ
支援役が修行場の準備
warrior1 の流れ
GokuWarriorが修行メニューを進める
warrior2 の流れ
VegetaWarriorが修行メニューを進めるこれらは、1本の一本道ではありません。
複数の道があり、それぞれが自分のタイミングで進みます。
| 表示の見え方 | 理由 |
|---|---|
| GokuWarrior が先に表示される | warrior1 のスレッドが先に進んだ |
| VegetaWarrior が先に表示される | warrior2 のスレッドが先に進んだ |
| 支援役が先に表示される | main のスレッドが先に進んだ |
| 順番が毎回違う | 実行のタイミングが固定されない |
ドラゴンボール風に言えば、GokuWarrior、VegetaWarrior、支援役がそれぞれ別の場所で動いています。
誰の報告が先に本部へ届くかは、そのときの動き方によって変わります。
だから、表示順が毎回同じでないことは、複数スレッドでは自然な動きです。
3つの流れが存在する理由
このプログラムで3つの流れになる理由を、もう一度整理します。
main は、プログラム開始時から1つのスレッドとして動いています。
そこに、1回目の start があります。
warrior1.start();これで、GokuWarrior のスレッドが増えます。
さらに、2回目の start があります。
warrior2.start();これで、VegetaWarrior のスレッドも増えます。
つまり、次のようになります。
| きっかけ | 増える流れ |
|---|---|
| main の開始 | main のスレッド |
| warrior1.start() | GokuWarrior のスレッド |
| warrior2.start() | VegetaWarrior のスレッド |
合計で3本です。
main
warrior1
warrior2このように、start を呼ぶたびに、新しい処理の流れが増えていきます。

mainも1つのスレッドである
複数スレッドを考えるとき、新しく作ったスレッドだけに注目しがちです。
しかし、main も1つのスレッドです。
main メソッドは、Javaプログラムの開始地点です。
その main の処理も、1つの処理の流れとして動いています。
| スレッド | 内容 |
|---|---|
| main スレッド | Sample2 の main メソッドを実行する |
| warrior1 スレッド | GokuWarrior の run を実行する |
| warrior2 スレッド | VegetaWarrior の run を実行する |
この考え方が分かると、複数スレッドの全体像が見えやすくなります。
スレッドを追加するというのは、main とは別の処理の流れを増やすことです。
main が消えるわけではありません。
main も動きます。
追加されたスレッドも動きます。
それぞれのスレッドはrunが終わると終了する
各スレッドは、自分の run メソッドが終わると終了します。
WarriorWorker の run は、for 文で5回メッセージを表示します。
public void run()
{
for(int i = 0; i < 5; i++){
System.out.println(name + "が修行メニューを進めています。");
}
}この for 文が終わると、そのスレッドの仕事は終わります。
| スレッド | 終了するタイミング |
|---|---|
| warrior1 | GokuWarrior の run が終わったとき |
| warrior2 | VegetaWarrior の run が終わったとき |
| main | main の for 文が終わったとき |
片方のスレッドが終わっても、他のスレッドがまだ動いていれば、その処理は続きます。
ドラゴンボール風に言えば、GokuWarrior の修行が先に終わっても、VegetaWarrior がまだ訓練中なら VegetaWarrior の流れは続きます。
支援役の準備が先に終わっても、GokuWarrior や VegetaWarrior の修行が終わるまで、それぞれの任務は続きます。
複数スレッドで気をつけたい見方
複数スレッドを扱うと、出力結果を見て「順番どおりに動いていない」と感じることがあります。
しかし、それは間違いではありません。
むしろ、複数スレッドらしい自然な動きです。
1本の流れだけなら、表示順はコードの順番にかなり近くなります。
しかし、3本の流れがある場合、次のような表示の混ざり方が起こります。
| 表示の混ざり方 | 起こる理由 |
|---|---|
| main の表示が続く | main が先に進んだ |
| GokuWarrior の表示が続く | warrior1 が先に進んだ |
| VegetaWarrior の表示が途中に入る | warrior2 がそのタイミングで進んだ |
| 毎回違う順番になる | 実行タイミングが変わる |
大切なのは、複数スレッドでは 細かな実行順序を固定して考えない ことです。
それぞれのスレッドは独立した流れとして動きます。
図:3本の流れと表示順が混ざる理由

この図が示していること
この図は、main、warrior1、warrior2 の3本の処理の流れが、それぞれ独立して進むことを表しています。
3本の流れから画面へ出力が届くため、表示順は1本の流れのように固定されません。
| 図の要素 | 意味 |
|---|---|
| mainスレッド | 支援役の準備処理 |
| warrior1スレッド | GokuWarrior の修行処理 |
| warrior2スレッド | VegetaWarrior の修行処理 |
| 画面出力 | 3本の流れから届いた表示結果 |
| 混ざった出力 | 複数スレッドの自然な見え方 |
この図から分かることは、複数スレッドでは、各処理が別々のタイミングで進むため、画面に出る順番も毎回同じとは限らないということです。
複数スレッドを使うと何がうれしいのか
複数スレッドを使うと、複数の処理を並行して進められます。
たとえば、次のような場面で考え方が役立ちます。
| 場面 | 複数スレッドの価値 |
|---|---|
| 複数の重い処理を進めたい | 作業を分けて進められる |
| 通信を待ちながら別処理をしたい | 待ち時間に別の流れを動かせる |
| 画面表示を止めたくない | 重い処理と表示処理を分けやすい |
| 複数の担当を並行させたい | 処理の役割分担ができる |
ドラゴンボール風に言えば、1人の戦士がすべてを抱えるのではなく、GokuWarrior は重力修行、VegetaWarrior は戦闘フォーム確認、支援役は修行場準備、通信係は本部連絡というように、任務を分担できます。
この分担によって、プログラム全体の動きに柔軟さが出ます。
ただし、複数スレッドでは順番が固定されないため、複数のスレッドが同じデータを扱う場合には注意が必要です。
今回の段階では、まず次のことを押さえれば十分です。
| ポイント | 内容 |
|---|---|
| スレッドは複数起動できる | start を複数回呼ぶと、その分だけ流れが増える |
| main も1つのスレッド | 新しいスレッドだけでなく main も動いている |
| 同じクラスから複数作れる | 同じ Thread 継承クラスから複数のオブジェクトを作れる |
| 実行順序は固定ではない | 出力順は環境やタイミングで変わる |
| run が終われば終了 | 各スレッドは自分の run が終わると終了する |
ドラゴンボール風に整理する
今回の内容をドラゴンボール風に整理すると、次のようになります。
main の流れでは、本部の支援役が修行場を準備しています。
そこに warrior1.start() によって、GokuWarrior という戦士が別の修行へ向かいます。
さらに warrior2.start() によって、VegetaWarrior という別の戦士も別の修行へ向かいます。
| Java | ドラゴンボール風のイメージ |
|---|---|
| main のスレッド | 本部側で修行場を準備する流れ |
| warrior1 | GokuWarrior という戦士オブジェクト |
| warrior2 | VegetaWarrior という戦士オブジェクト |
| warrior1.start() | GokuWarrior への出撃命令 |
| warrior2.start() | VegetaWarrior への出撃命令 |
| run | それぞれの修行内容 |
| 出力順が変わる | 報告が届く順番が毎回変わる |
複数スレッドを動かすというのは、プログラムの中で複数の戦士がそれぞれの任務を進める状態を作ることです。
start を1回呼べば、新しい流れが1本増えます。
start を2回呼べば、新しい流れが2本増えます。
そして、もともとの main の流れも動いているため、今回の Sample2.java では合計3本の流れが動きます。
この感覚がつかめると、この先で学ぶ一時停止、終了待ち、Runnable インターフェイス、同期処理も理解しやすくなります。
