はじめに
Javaのマルチスレッドプログラミングは、複数のタスクを並行して実行し、効率的なアプリケーションを構築するための重要なスキルです。特に、CPUのリソースを最大限活用し、処理速度を向上させるために欠かせない技術です。
この記事では、マルチスレッドの基本概念 から、スレッドの作成方法、同期、実践的なサンプルコード までを徹底解説します。Javaスペシャリストも納得できる深い内容を初心者向けに分かりやすくお届けします。
1. マルチスレッドとは?
マルチスレッドとは、1つのプログラム内で 複数のスレッド を並行して動作させる仕組みです。
- プロセス: 実行中のプログラムのこと。
- スレッド: プロセス内で並行して動作する小さな単位。
シングルスレッド の場合は1つのタスクを順番に処理しますが、マルチスレッド を使うことで複数のタスクを同時に実行できます。
マルチスレッドのメリット
- 処理速度の向上: CPUのコアをフル活用。
- 効率的なリソース利用: I/O待ち時間を短縮。
- UIの応答性向上: GUIアプリケーションなどでの並列処理。
2. スレッドの作成方法
Javaでは、スレッド を作成する主な方法が2つあります。
- Threadクラスを継承する方法
- Runnableインターフェースを実装する方法
2.1 Threadクラスを継承する方法
Thread クラスを継承し、run() メソッドをオーバーライドしてスレッドの処理を記述します。
サンプルコード
class MyThread extends Thread {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Thread: " + i);
try {
Thread.sleep(500); // 500ミリ秒スリープ
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadExample {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start(); // スレッド開始
t2.start();
}
}
ポイント
run()メソッド: スレッドの実行内容を記述。start()メソッド: スレッドを開始。
2.2 Runnableインターフェースを実装する方法
Runnable インターフェースを実装し、run() メソッドを定義します。
サンプルコード
class MyRunnable implements Runnable {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Runnable: " + i);
try {
Thread.sleep(500); // 500ミリ秒スリープ
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class RunnableExample {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
Thread t2 = new Thread(new MyRunnable());
t1.start();
t2.start();
}
}
ポイント
- Runnableの利点: 他のクラスを継承できる(Javaでは多重継承不可のため)。
Threadクラスのコンストラクタ にRunnableを渡す。
3. スレッドの同期(Synchronization)
マルチスレッドでは、複数のスレッドが同じリソース(例: 変数)を同時に操作すると 競合 が発生します。これを防ぐために、同期処理 が必要です。
同期の方法: synchronizedキーワード
synchronized キーワードを使って、1つのスレッド だけがメソッドやブロックにアクセスできるようにします。
サンプルコード(同期処理)
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class SyncExample {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Final Count: " + counter.getCount());
}
}
ポイント
synchronized: 同時アクセスを防ぎ、1つのスレッドのみがメソッドを実行可能。join(): スレッドが終了するまでメインスレッドを待機させる。
4. スレッドプールの活用
スレッドを大量に作成するとオーバーヘッドが発生するため、スレッドプール を使用して効率的にスレッドを管理します。
Executorフレームワーク
Javaの ExecutorService を使用して、スレッドプールを簡単に実装できます。
サンプルコード
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 1; i <= 5; i++) {
final int task = i;
executor.execute(() -> {
System.out.println("Task " + task + " is running on " + Thread.currentThread().getName());
});
}
executor.shutdown();
}
}
ポイント
Executors.newFixedThreadPool(n): n個のスレッドを持つスレッドプールを作成。shutdown(): スレッドプールを終了。
5. スレッドのライフサイクル
スレッドのライフサイクルには以下の5つの状態があります。
- NEW: スレッドが作成されたが、まだ
start()が呼び出されていない状態。 - RUNNABLE: スレッドが実行可能状態。
- RUNNING: スレッドが実行中。
- BLOCKED/WAITING: スレッドがリソース待ちの状態。
- TERMINATED: スレッドの処理が終了。
6. マルチスレッドの注意点とベストプラクティス
- 競合状態を避ける: 共有リソースは synchronized で保護。
- デッドロックの回避: スレッド間のロック順序に注意する。
- スレッド数の制限: スレッドプールを利用して効率的に管理する。
- 例外処理: スレッド内での例外は適切にキャッチしてログ出力する。
7. まとめ
Javaのマルチスレッドプログラミングは、効率的な処理や高速なアプリケーションを構築するために欠かせない技術です。
- Threadクラス と Runnableインターフェース の使い分け。
- 同期処理 でリソース競合を防止。
- Executorフレームワーク を活用してスレッド管理を効率化。
基本をしっかり押さえ、実践を通してスキルを磨いていきましょう!
次のステップ
より深く学びたい方は、絶対にJavaプログラマーになりたい人へ を参考にするか、サイゼントアカデミー の学習サポートを活用し、確実にスキルを身につけましょう!
入門ガイド-120x68.webp)

コメント