スレッドとは

スレッド(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クラスを継承したら、Threadを実行する「run」メソッドの実装が必要です。

public class クラス名 implements Runnable {
    public void run() {
        
    }
}

シングルスレッド処理

sleep()

「sleep」メソッドは、指定した時間でプログラムを待機します。

Thread.sleep(時間);

sleep() は例外処理

「sleep」メソッドを利用するときは、「try-catch」で例外処理が必要です。

ファイル構成

./
└── src/
  └── thread/
    ├── Item.java
    └── AppSleep.java

Itemクラス作成

「Item」クラスを作成し、「name」プロパティと「order」メソッドを追加します。「order」メソッドでは「sleep」メソッドで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("---注文完了---");
    }
}

メインプログラム作成

「AppSleep」で「Item」インスタンスを3つ作成し、繰り返し処理をします。

package thread;

import java.util.ArrayList;

public class AppSleep {

	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();
		}
	}

}

実行結果

コーヒー
---注文完了---
紅茶
---注文完了---
ほうじ茶
---注文完了---

マルチスレッドの実行

ファイル構成

./
└── src/
  └── thread/
    ├── Item.java
    ├── AppOrder.java
    └── ShopThread.java

Threadクラス作成

Threadクラス継承

「ShopThread」クラスを作成して「Thread」クラスを継承します。「order」メソッドにはコンストラクタで入力された文字列をsleepしながら表示します。

ShopThread.java
package thread;

public class ShopThread extends Thread {
	
	private Item item;

	ShopThread(Item item) {
		this.item = item;
	}
}

run() の実装

Threadで実行される「run」メソッドで、「order」メソッドを実行します。

ShopThread.java
    public void run() {
    	item.order();
    }

メインアプリの作成

メインアプリ「AppOrder」クラスで、「ShopThread」インスタンスのThreadを開始します。

AppOrder.java
package thread;

public class App {
	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();
		}
	}
}

実行確認

「AppOrder」を実行すると、他のスレッドの処理が並行して処理される非同期処理ということがわかります。非同期処理は実行順が保証されていません。

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();
			}
		}
	}

実行確認

「AppShop」を実行すると順番に処理され、「run」メソッドが終了するまで待機していることがわかります。

コーヒーを注文しました
---注文完了---
紅茶を注文しました
---注文完了---
ほうじ茶を注文しました
---注文完了---

スレッドの同期

synchronized

「synchronized」修飾子は指定したオブジェクトやメソッドをロックし、他のスレッドが終了するまで実行できない同期処理を実現します。

synchronized void method() {

}

synchronized (オブジェクト);

ファイル構成

./
└── src/
  └── thread/
    ├── AppCounter.java
    └── CounterThread.java

非同期処理の場合

「CounterThread」でカウントダウンするプログラムを作成します。スレッド処理は「Runnable」で実装します。

CounterThread.java
package thread;

public class CounterThread implements Runnable {

	public static Integer count = 5;
	public long ms = 1000;

	@Override
	public void run() {
	    while (count > 0) {
	    	try {
	    	    Thread.sleep(ms);
	    	    System.out.println(Thread.currentThread().getName() + " : " + count);
	    	    count--;
	    	} catch (InterruptedException e) {
				System.out.println("Error");
	    	}
	    }
	    count = 5;
	    System.out.println("End thread");
	}

}

メインプログラム

「AppCounter」で、「CounterThread」スレッドを2つ実行します。

AppCounter.java
package thread;

public class AppCounter {

	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」プロパティを「syncronized」で同期処理します。

CounterThread.java
	public void run() {
		// 同期処理(ロック)
		synchronized (count) {
			System.out.println("Start thread");
			while (count > 0) {
				try {
					sleep(ms);
					System.out.println(this.getName() + " : " + count);
					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を利用する定義で正しいのはどれですか?

1)public class Calculate extends Thread { }

2)public class Thread extends Calculate { }

3)public class Calculate implements Thread { }

4)public class Thread implements Calculate { }

問題2

Threadを開始するメソッドはどれですか?

1)start()

2)run()

3)join()

4)sleep()

問題3

Threadが実行されるメソッドはどれですか?

1)start()

2)run()

3)join()

4)sleep()

問題4

Threadが終了するまで待機メソッドはどれですか?

1)start()

2)run()

3)join()

4)sleep()

問題5

同期処理の説明で正しいのはどれですか?

1)スレッドを並行処理できる

2)他のスレッドが終了するまで待ってから、スレッド処理をする

3)他のスレッド処理の終了を待たなくても、スレッドを開始できる処理

4)実行順が保証されない

問題6

非同期処理の説明で正しいのはどれですか?

1)スレッドを実行順に処理できる

2)他のスレッドが終了するまで待ってから、スレッド処理をする

3)他のスレッド処理の終了を待たなくてもスレッドを開始できる処理

4)synchronized を使って処理をする