スレッド(Threads)は、Javaでの並行処理を実現するための重要な概念です。並行処理を効果的に活用することで、複数のタスクを同時に実行し、プログラムのパフォーマンスを向上させることができます。Javaではスレッドを利用して、複数の処理を同時に実行することが可能です。この概念を理解することは、より効率的でパフォーマンスの高いアプリケーションを作成するために不可欠です。
スレッドとは?
スレッドとは、プロセス内で実行される最小の実行単位のことです。スレッドを使うことで、ひとつのプログラムが同時に複数の処理を実行できます。たとえば、GUIアプリケーションではユーザーの操作を受け付けながら、バックグラウンドで別の処理を行うことができます。Javaでは、スレッドを使って並行処理を実現することができます。
Javaでのスレッドの利用方法
Javaでスレッドを使用するには、いくつかの方法があります。主に以下の2つの方法が一般的に使われます。
1. Thread クラスを継承する方法
Thread クラスを直接継承し、そのクラスの run メソッドをオーバーライドする方法です。以下のコードは、この方法を使ってスレッドを作成する例です。
javaclass MyThread extends Thread {
public void run() {
System.out.println("スレッドが実行中です");
}
}
public class ThreadExample {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // スレッドを開始
}
}
ここで、run メソッドには、スレッドが実行する処理を記述します。そして、start() メソッドを呼び出すことで、スレッドが実行されます。重要なのは、start() メソッドを呼び出すことでスレッドが非同期で実行される点です。run() メソッドを直接呼び出してもスレッドは実行されませんので注意が必要です。
2. Runnable インターフェースを実装する方法
Javaでは、Runnable インターフェースを実装してスレッドを作成することもできます。この方法は、スレッドを作成するクラスが既に別のクラスを継承している場合に有効です。以下のコードは、Runnable インターフェースを使用してスレッドを作成する例です。
javaclass MyRunnable implements Runnable {
public void run() {
System.out.println("Runnableスレッドが実行中です");
}
}
public class ThreadExample {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable); // RunnableをThreadに渡す
thread.start(); // スレッドを開始
}
}
この方法では、Runnable インターフェースを実装したクラスを作成し、そのインスタンスを Thread のコンストラクタに渡してスレッドを開始します。Runnable を使うことで、スレッドの実行部分とスレッドの管理部分を分けて実装できます。
スレッドのライフサイクル
Javaのスレッドには、いくつかの状態(ライフサイクル)が存在します。これらの状態は、スレッドが実行される過程で変化します。主な状態は以下の通りです。
- 新規(New): スレッドが作成され、まだ開始されていない状態です。
- 実行中(Runnable): スレッドが実行を待っているか、または実行中の状態です。Javaでは、スレッドが実行可能状態にあるときは常にこの状態にあります。
- ブロック中(Blocked): 他のスレッドがリソースを占有しているため、実行できない状態です。
- 待機中(Waiting): 他のスレッドからの通知を待っている状態です。たとえば、
wait()メソッドが呼ばれたときです。 - タイムアウト待機中(Timed Waiting): 指定された時間が経過するのを待っている状態です。たとえば、
sleep()メソッドが使われる場合です。 - 終了(Terminated): スレッドが終了した状態です。
スレッドの同期
複数のスレッドが同じリソースを同時に操作すると、競合状態が発生することがあります。この競合状態を防ぐためには、スレッドの同期が必要です。Javaでは、synchronized キーワードを使って、メソッドまたはブロックを同期化できます。
javaclass Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
public class ThreadExample {
public static void main(String[] args) {
Counter counter = new Counter();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("カウント: " + counter.getCount());
}
}
この例では、increment メソッドに synchronized を付けることで、同時に2つのスレッドがカウントをインクリメントしないようにしています。join() メソッドは、メインスレッドが他のスレッドの終了を待機するために使用されます。
スレッドプールの利用
スレッドを毎回作成するのではなく、スレッドプールを利用して効率的にスレッドを管理することも重要です。ExecutorService を使用することで、スレッドプールを簡単に利用できます。
javaimport java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadExample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(() -> {
System.out.println("タスク1");
});
executorService.submit(() -> {
System.out.println("タスク2");
});
executorService.shutdown(); // 終了
}
}
ここでは、Executors.newFixedThreadPool(2) を使って、2つのスレッドを管理するスレッドプールを作成し、タスクをスレッドプールに渡して実行しています。スレッドプールを使うことで、スレッドの管理が簡単になり、パフォーマンスも向上します。
まとめ
Javaでのスレッドは、並行処理を実現するために非常に重要な機能です。スレッドをうまく活用することで、複数のタスクを同時に実行し、効率的なプログラムを作成できます。Thread クラスや Runnable インターフェースを使ったスレッドの作成方法、スレッドの同期、スレッドプールの利用など、さまざまな方法を駆使して、並行処理を効果的に利用することができます。

