Javaにおいて、スレッド(Thread)とはプログラム内で並行して実行される処理の最小単位を指します。効率的なアプリケーション開発には、マルチスレッドを活用した並列処理が欠かせません。本稿では、Javaでスレッドを生成・実行するための主要な3つの方法を解説します。
1. Threadクラスを継承する方法
最も基本的な方法は、java.lang.Threadクラスを継承して新しいクラスを作成することです。このクラスでrun()メソッドをオーバーライドし、スレッドで実行したい処理を記述します。
/**
* Threadクラスを継承したカスタムスレッドクラス
*/
class CustomWorker extends Thread {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + " が処理中: " + i);
}
}
}
public class ThreadExample {
public static void main(String[] args) {
System.out.println("メインスレッド開始");
// インスタンス化してstart()を呼び出す
CustomWorker worker = new CustomWorker();
worker.setName("SubThread-1");
worker.start();
System.out.println("メインスレッド終了");
}
}
注意点: run()メソッドを直接呼び出してはいけません。run()を直接呼ぶと、新しいスレッドは作成されず、現在のメインスレッド内で通常のメソッドとして実行されてしまいます。必ずstart()メソッドを使用してスレッドを起動してください。
2. Runnableインターフェースを実装する方法
Javaは多重継承をサポートしていないため、他のクラスを継承する必要がある場合は、Runnableインターフェースを実装する方法が推奨されます。これはタスクの「実行内容」と「スレッド本体」を分離できるため、より柔軟な設計が可能です。
/**
* Runnableインターフェースを実装したタスク
*/
class TaskLogic implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " でタスクを実行しています。");
}
}
public class RunnableExample {
public static void main(String[] args) {
// Runnableの実装クラスを使用
TaskLogic logic = new TaskLogic();
Thread t1 = new Thread(logic, "LogicThread");
t1.start();
// 匿名内部クラスを使用
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名クラススレッド実行中");
}
}).start();
// ラムダ式を使用(Java 8以降)
new Thread(() -> {
System.out.println("ラムダ式スレッド実行中");
}).start();
}
}
3. CallableとFutureTaskを使用する方法(戻り値あり)
前述の2つの方法は、スレッドの実行結果を呼び出し元に返すことができません。処理結果を取得したい場合は、JDK 5で導入されたjava.util.concurrent.CallableインターフェースとFutureTaskクラスを使用します。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 戻り値を持つタスク(1からnまでの合計を計算)
*/
class SumTask implements Callable<Integer> {
private int limit;
public SumTask(int limit) {
this.limit = limit;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= limit; i++) {
sum += i;
}
return sum;
}
}
public class CallableExample {
public static void main(String[] args) {
// Callableオブジェクトの作成
SumTask task = new SumTask(100);
// FutureTaskでラップする
FutureTask<Integer> futureTask = new FutureTask<>(task);
// Threadに渡して実行
new Thread(futureTask).start();
try {
// get()メソッドで結果を取得。完了するまでブロックされる
Integer result = futureTask.get();
System.out.println("計算結果: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
Callableのcall()メソッドは戻り値を返せるだけでなく、チェック例外(Exception)をスローすることも可能です。これにより、より高度な非同期処理のエラーハンドリングが可能になります。