スレッドと並列処理は、Javaにおける非常に重要なコンセプトであり、マルチタスクを効率的に処理するための強力なツールです。Javaでは、スレッドを使ってプログラムの複数の部分を同時に実行することができます。この技術は、特にパフォーマンスを向上させるために重要です。この記事では、Javaにおけるスレッドの基本的な概念から並列処理の実現方法、そして実際のコード例を交えて解説します。
スレッドとは?
スレッドとは、プロセス内で実行される最小の実行単位です。Javaでのスレッドは、通常のプログラムの流れとは独立して並行して実行されます。これにより、複数のタスクを同時に処理することが可能になります。

スレッドの作成方法
Javaでスレッドを作成するには、2つの方法があります:
Thread
クラスを拡張する方法Runnable
インターフェースを実装する方法
1. Thread
クラスを拡張する方法
最も基本的な方法は、Thread
クラスを拡張し、そのrun
メソッドをオーバーライドすることです。このメソッドにはスレッドが実行するコードを書きます。
javaclass MyThread extends Thread {
public void run() {
System.out.println("スレッドが実行されています");
}
}
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // スレッドの開始
}
}
上記のコードでは、MyThread
クラスがThread
クラスを拡張し、run
メソッドにスレッドが実行する処理を記述しています。start
メソッドを呼び出すことでスレッドが実行されます。
2. Runnable
インターフェースを実装する方法
もう一つの方法は、Runnable
インターフェースを実装して、run
メソッドを定義することです。この方法では、スレッドを別のクラスから独立して実行できるため、コードの再利用性が向上します。
javaclass MyRunnable implements Runnable {
public void run() {
System.out.println("スレッドが実行されています");
}
}
public class Main {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start(); // スレッドの開始
}
}
この方法の利点は、Runnable
インターフェースを複数のスレッドで共有できる点です。異なるクラスで同じrun
メソッドを使い回すことができます。
スレッドの同期
複数のスレッドが同じリソースにアクセスすると、データの整合性が保たれない場合があります。これを防ぐために、スレッド間で同期を行うことが重要です。
Javaでは、synchronized
キーワードを使ってメソッドやブロックを同期することができます。同期されたメソッドは、1つのスレッドのみが同時に実行できるようになります。
javaclass Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
public class Main {
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();
}
}
この例では、increment
メソッドにsynchronized
をつけて、複数のスレッドが同時にこのメソッドを実行しないようにしています。
スレッドプール
スレッドの生成にはオーバーヘッドがかかるため、頻繁にスレッドを生成することは効率的ではありません。これを改善するために、Javaはスレッドプールを提供しています。スレッドプールを使用することで、あらかじめ決められた数のスレッドをプールしておき、タスクが来た際にその中のスレッドを再利用することができます。
javaimport java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
executorService.submit(() -> {
System.out.println("スレッドが実行されています");
});
}
executorService.shutdown();
}
}
このコードでは、ExecutorService
を使ってスレッドプールを作成し、その中のスレッドを使って100回のタスクを並列に実行しています。タスクが完了した後、shutdown
メソッドでスレッドプールを終了します。
並列処理の実現
並列処理とは、複数のタスクを並行して処理することです。Javaでは、ForkJoinPool
を使うことで、より効率的に並列処理を行うことができます。ForkJoinPool
は、大きなタスクを小さなタスクに分割し、それらを並列に実行します。
javaimport java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinPool;
public class Main {
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool();
// 再帰的にタスクを分割する
RecursiveTask task = new RecursiveTask<>() {
@Override
protected Integer compute() {
return 1 + 1; // 単純なタスク例
}
};
System.out.println(pool.invoke(task)); // タスクの結果を取得
}
}
ForkJoinPool
を使用すると、大規模な並列処理が効率的に実行できます。
結論
Javaにおけるスレッドと並列処理は、性能を最適化し、効率的なマルチタスク処理を実現するための強力なツールです。スレッドの作成方法、同期、スレッドプールの使用、そして並列処理を適切に活用することで、アプリケーションのパフォーマンスを大幅に向上させることができます。