プロセスとスレッドの概念
プロセスとスレッドは並行処理の基本単位です。例えば動画再生アプリケーションでは、アプリ起動がプロセスに対応し、映像処理、音声再生、コメント表示などの各機能が個別のスレッドで実行されます。プロセスは実行中のプログラムでシステムリソース割当の基本単位、スレッドはCPUスケジューリングの基本単位です。
重要な特性
- スレッドは独立した実行パスを形成
- 明示的にスレッドを作成しなくてもmainスレッド(ユーザースレッド)やGCスレッド(デーモンスレッド)が存在
- 同一リソースへのアクセス時には競合状態が発生する可能性あり
- 各スレッドは固有の作業領域を持ち、データ不整合のリスクあり
スレッド生成方法
Javaにおけるスレッド生成の主要な手法:
- Threadクラスの継承とrun()メソッドのオーバーライド
- Runnableインターフェースの実装とThreadへの渡し
- Callableインターフェースの実装とFutureTaskの利用
実装上の注意点
- スレッド起動は即時実行を保証せずCPUスケジューリングに依存
- run()メソッド直接呼び出しではスレッド生成されない
- Runnable実装が推奨(単一継承制約回避のため)
- Callable実装では戻り値と例外処理が可能
スレッド状態遷移
スレッドライフサイクルの基本状態:
| 状態 | 説明 |
|---|---|
| NEW | Threadインスタンス生成直後 |
| RUNNABLE | start()呼び出し後 |
| RUNNING | CPUリソース獲得中 |
| BLOCKED | sleep/wait/lockによる待機 |
| TERMINATED | 実行完了後 |
基本操作メソッド
- sleep(): 指定時間スレッド停止
- yield(): 実行権を他スレッドへ譲渡
- join(): 他スレッドの終了待機
- interrupt(): スレッド中断(非推奨)
スレッド制御実装例
安全なスレッド停止
public class SafeTermination implements Runnable {
private volatile boolean active = true;
public void run() {
int counter = 0;
while(active) {
System.out.println("実行中: " + counter++);
}
}
public void terminate() {
active = false;
}
public static void main(String[] args) {
SafeTermination task = new SafeTermination();
new Thread(task).start();
for(int i=0; i<=100; i++) {
System.out.println("メイン: " + i);
if(i == 90) {
task.terminate();
System.out.println("停止指示発行");
}
}
}
}
スレッド優先制御
public class PriorityControl {
public static void main(String[] args) throws Exception {
Thread vip = new Thread(() -> {
for(int i=0; i<100; i++) {
try {
Thread.sleep(10);
System.out.println("優先処理: " + i);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
});
vip.start();
for(int j=0; j<100; j++) {
if(j == 50) vip.join();
System.out.println("通常処理: " + j);
}
}
}
スレッド同期機構
共有リソースアクセス時の競合回避策:
- synchronizedキーワードによるメソッド/ブロック同期
- 明示的ロック(Lockインターフェース)
- アトミック操作(Atomicクラス群)
デッドロック発生条件
- 相互排除: リソース排他制御
- 保持待機: 獲得リソース保持中の追加要求
- 強制解除不可: リソースの強制解放不可
- 循環待機: スレッド間の循環依存