32.
Thread
スレッドとは
Thread
Thread(スレッド)は「糸」という意味で、プログラム処理の実行単位を指します。プログラム処理は「一本の糸」のように連続実行されます。
同期処理
スレッドを順に実行していくことを同期処理(Synchronous processing) といいます。同期処理は重たい処理が間にあると待つ必要があるので、処理に時間がかかります。

並列処理
プログラムを同時に処理することができることを並列処理(マルチスレッド) といいます。

非同期処理
他の処理の終了を待たなくてもスレッドを開始できる処理を非同期処理(Asynchronous processing ) といいます。メインスレッドから複数のスレッドを連続で開始でき、各スレッドが終了するまでは待ちます。

シングルスレッドとマルチスレッド
プログラムが1つのスレッドで動作することをシングルスレッドといい、プログラムが並行処理で複数スレッドで動作することをマルチスレッドといいます。
並行処理で時間効率をあげる
通常のプログラム処理は、処理が終わるまで待たなければいけませんが、スレッドを使うと処理を並行して実行できるので、時間効率が上がります。
Threadの基本
JavaのThreadクラス
JavaのThreadクラスは「java.lang.Thread」で利用します。Threadクラスには以下のメソッドがあります。
メソッド | 説明 |
---|---|
Thread() | Threadコンストラクタ。引数にスレッド名も指定できる |
start() | スレッド処理を開始 |
run() | スレッド開始時に実行されるオーバーライドメソッド |
join() | スレッドが終了するまで待機 |
sleep(long millis) | 指定時間(ms)でスレッドを一時停止するstaticメソッド |
yield() | 実行中のスレッドを休止して、他のスレッドに移行する |
setName() | スレッド名を設定 |
getName() | スレッド名を取得 |
currentThread() | 現在のThreadインスタンスを取得するstaticメソッド |
Threadの定義
Threadを利用するには、Threadクラスを継承する方法と、Runnableインターフェースを実装する方法があります。
Threadクラスの継承
public class クラス名 extends Thread {
public void run() {
}
}
Runnableインターフェイス
Threadクラスを継承したら、スレッドを実行するためのrunメソッドの実装が必要です。
public class クラス名 implements Runnable {
public void run() {
}
}
シングルスレッド処理
sleep()
sleepメソッドは、指定した時間でプログラムを待機します。
Thread.sleep(時間);
sleep() は例外処理
sleepメソッドを利用するときは、try-catchで例外処理が必要です。

ファイル構成
thread/
├── Item.java
└── SleepApp.java
Itemクラス作成
Itemクラスを作成し、nameプロパティとorderメソッドを追加し、orderメソッド1秒待機します。
Item.java
package thread;
public class Item {
public String name;
Item(String name) {
this.name = name;
}
public void order() {
try {
System.out.println(name);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("---注文完了---");
}
}
メインプログラム作成
メインアプリでItemインスタンスを3つ作成し、繰り返し処理をします。
SleepApp.java
package thread;
import java.util.ArrayList;
public class SleepApp {
public static void main(String[] args) {
ArrayList<Item> items = new ArrayList<>();
items.add(new Item("コーヒー"));
items.add(new Item("紅茶"));
items.add(new Item("ほうじ茶"));
for (Item item : items) {
item.order();
}
}
}
実行結果
コーヒー
---注文完了---
紅茶
---注文完了---
ほうじ茶
---注文完了---
マルチスレッドの実行
ファイル構成
thread/
├── Item.java
├── OrderApp.java
└── ShopThread.java
Threadクラス作成
Threadクラス継承
ShopThreadクラスを作成して、Threadクラスを継承します
ShopThread.java
package thread;
public class ShopThread extends Thread {
private Item item;
ShopThread(Item item) {
this.item = item;
}
}
run() の実装
Threadには、*run()*を実装する必要があります。Threadが開始すると run() が自動実行されます。
ShopThread.java
package thread;
public class ShopThread extends Thread {
private Item item;
ShopThread(Item item) {
this.item = item;
}
public void run() {
item.order();
}
}
メインアプリの作成
メインアプリ「AppOrder」クラスで、「ShopThread」インスタンスのThreadを開始します。
OrderApp.java
package thread;
public class OrderApp {
public static void main(String[] args) {
ArrayList<Item> items = new ArrayList<>();
items.add(new Item("コーヒー"));
items.add(new Item("紅茶"));
items.add(new Item("ほうじ茶"));
for (Item item : items) {
ShopThread thread = new ShopThread(item);
thread.start();
}
}
}
実行確認
メインアプリを実行すると、他のスレッドの処理が並行して処理される非同期処理ということがわかります。非同期処理は実行順が保証されていません。
1回目
コーヒー
紅茶
ほうじ茶
---注文完了---
---注文完了---
---注文完了---
2回目
紅茶
コーヒー
ほうじ茶
---注文完了---
---注文完了---
---注文完了---
joinメソッド
joinメソッドは、スレッド処理が終了するまで待機します。joinメソッドを実行するには例外処理が必要です。
try {
スレッド.join();
} catch (InterruptedException e) {
}
join() の追加
スレッド実行を joinメソッドで待機してみましょう。
public static void main(String[] args) {
ArrayList<Item> items = new ArrayList<>();
items.add(new Item("コーヒー"));
items.add(new Item("紅茶"));
items.add(new Item("ほうじ茶"));
for (Item item : items) {
ShopThread thread = new ShopThread(item);
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
実行確認
メインアプリを実行すると順番に処理され、runメソッドが終了するまで待機していることがわかります。
コーヒーを注文しました
---注文完了---
紅茶を注文しました
---注文完了---
ほうじ茶を注文しました
---注文完了---
スレッドの同期
synchronized
synchronizedキーワードは指定したオブジェクトやメソッドをロックし、他のスレッドが終了するまで実行できない同期処理を実現します。
synchronized void method() {
}
synchronized (オブジェクト);
ファイル構成
thread/
├── CounterApp.java
└── CounterThread.java
非同期処理の場合
CounterThreadでカウントダウンするプログラムを作成します。スレッド処理はRunnableで実装します。
CounterThread.java
package thread;
public class CounterThread implements Runnable {
public static Integer count = 5;
public long interval = 1000;
@Override
public void run() {
while (count > 0) {
try {
Thread.sleep(interval);
String message = Thread.currentThread().getName() + " : " + count;
System.out.println(message);
count--;
} catch (InterruptedException e) {
System.out.println("Error");
}
}
count = 5;
System.out.println("End thread");
}
}
メインプログラム
メインプログラムでCounterThreadスレッドを2つ実行します。
CounterApp.java
package thread;
public class CounterApp {
public static void main(String[] args) {
CounterThread counter = new CounterThread();
Thread thread1 = new Thread(counter);
Thread thread2 = new Thread(counter);
thread1.setName("スレッド1");
thread2.setName("スレッド2");
thread1.start();
thread2.start();
}
}
実行確認
「AppCounter」アプリを実行すると、カウントダウンしますが、数値が順番にならず毎回結果も異なります。
スレッド2 : 5
スレッド1 : 5
スレッド2 : 3
スレッド1 : 3
スレッド2 : 1
End thread
スレッド1 : 1
End thread
...
同期処理の場合
runメソッドでcountプロパティを同期処理します。
CounterThread.java
public void run() {
// 同期処理(ロック)
synchronized (count) {
System.out.println("Start thread");
while (count > 0) {
try {
sleep(interval);
String message = Thread.currentThread().getName() + " : " + count;
System.out.println(message);
count--;
} catch (InterruptedException e) {
System.out.println("Error");
}
}
count = 5;
System.out.println("End thread");
}
}
実行確認
スレッド1→スレッド2と同期処理になり、カウントダウンも順番に表示されます。
スレッド1 : 5
スレッド1 : 4
スレッド1 : 3
スレッド1 : 2
スレッド1 : 1
End thread
スレッド2 : 5
スレッド2 : 4
スレッド2 : 3
スレッド2 : 2
スレッド2 : 1
End thread
演習
問題1
「Calculate」クラスでThreadを利用する定義で正しいのはどれですか?
- public class Calculate extends Thread { }
- public class Thread extends Calculate { }
- public class Calculate implements Thread { }
- public class Thread implements Calculate { }
問題2
Threadを開始するメソッドはどれですか?
- start()
- run()
- join()
- sleep()
問題3
Threadが開始すると自動実行されるメソッドはどれですか?
- start()
- run()
- join()
- sleep()
問題4
Threadの処理が完了するまで待機メソッドはどれですか?
- start()
- run()
- join()
- sleep()
問題5
同期処理の説明で正しいのはどれですか?
- スレッドを並行処理できる
- 他のスレッドが終了するまで待ってから、スレッド処理をする
- 他のスレッド処理の終了を待たなくても、スレッドを開始できる処理
- 実行順が保証されない
問題6
非同期処理の説明で正しいのはどれですか?
- スレッドを実行順に処理できる
- 他のスレッドが終了するまで待ってから、スレッド処理をする
- 他のスレッド処理の終了を待たなくてもスレッドを開始できる処理
- synchronized を使って処理をする