1. java waitとは何か(結論を最短で理解する)
java wait は、あるスレッド(処理の流れ)を一時停止し、他のスレッドからの通知を待つためのメソッドです。
マルチスレッド処理(複数の処理を同時に動かす仕組み)で、スレッド間の協調制御を行うために使用します。
重要なのは、waitは「単なる時間待ち」ではないという点です。
他スレッドから notify() または notifyAll() が呼ばれるまで、対象スレッドは待機状態(WAITING)になります。
1.1 waitの基本的な意味
waitの本質は次の3点です。
- 現在のスレッドを待機状態にする
- 対象オブジェクトのロックを一時的に解放する
- 他スレッドからの通知で再開する
概念図(流れ):
- synchronizedブロックに入る
wait()を呼ぶ- ロックを解放して待機状態へ
- 他スレッドが
notify()を呼ぶ - ロックを再取得後に処理再開
簡単な構文例:
synchronized(obj) {
obj.wait();
}
⚠ 注意
- waitは「一定時間止まる」のではなく、「通知を待つ」仕組みです。
- 何も通知されなければ永遠に待ち続けます。
1.2 waitはObjectクラスのメソッド
waitは Thread クラスのメソッドではありません。
Objectクラスのメソッドです。
定義(概念):
public final void wait()
つまり、
- すべてのJavaオブジェクトはwaitを持っている
- 監視対象となるオブジェクトを基準に待機する
よくある誤解:
❌ Thread.wait() と思っている
❌ スレッドを直接止めるメソッドだと思っている
正しくは:
✔ 「オブジェクトのモニター(監視ロック)上で待機する」
※モニター=synchronizedで管理されるロックのこと。
1.3 waitを使うための絶対条件
waitを使うには、必ず synchronized ブロック内で呼び出す必要があります。
理由:
- waitは「現在保持しているロック」を解放する動作をするため
- ロックを保持していないと整合性が取れない
誤った例:
Object obj = new Object();
obj.wait(); // ❌ IllegalMonitorStateException
正しい例:
Object obj = new Object();
synchronized(obj) {
obj.wait();
}
発生する例外:
IllegalMonitorStateException
これは「ロックを持っていないのにwaitを呼んだ」ことを意味します。
⚠ 初心者が特につまずくポイント
- sleepと混同する(sleepはロックを解放しない)
- synchronizedを書き忘れる
- notifyを呼ばないため永遠に停止する
- 別のオブジェクトに対してnotifyしてしまう
✔ ここまでの最重要まとめ
- waitはObjectクラスのメソッド
- synchronized内でのみ使用可能
- ロックを解放して通知を待つ仕組み
- 単なる時間待ちではない
2. waitの正しい使い方(コード例で理解する)
waitは単体では意味を持ちません。
notify / notifyAll と組み合わせて初めて機能する仕組みです。
この章では、最小構成から実践パターンまで段階的に説明します。
2.1 基本構文
waitを正しく使う最小構成は次の通りです。
Object lock = new Object();
synchronized(lock) {
lock.wait();
}
ただし、このままでは永遠に停止します。
理由:通知(notify)が存在しないため。
2.2 notifyとの組み合わせ例(最重要)
waitは「通知待ち」です。
したがって、別スレッド側で notify を呼び出す必要があります。
✔ 最小動作例
class Example {
private static final Object lock = new Object();
public static void main(String[] args) {
Thread waitingThread = new Thread(() -> {
synchronized(lock) {
try {
System.out.println("Waiting...");
lock.wait();
System.out.println("Resumed.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread notifyingThread = new Thread(() -> {
synchronized(lock) {
System.out.println("Notifying...");
lock.notify();
}
});
waitingThread.start();
notifyingThread.start();
}
}
処理の流れ
- waitingThreadがlockを取得
- wait()でロックを解放し待機
- notifyingThreadがlockを取得
- notify()で待機スレッドに通知
- ロックを再取得後に処理再開
2.3 notifyとnotifyAllの違い
| メソッド | 動作 |
|---|---|
| notify() | 待機スレッドを1つだけ再開 |
| notifyAll() | すべての待機スレッドを再開 |
実務では notifyAll() の方が安全 とされる場合が多いです。
理由:
- notify()では、再開すべきでないスレッドが選ばれる可能性がある
- 条件が複数存在する場合に不整合が起きやすい
2.4 正しい実践パターン(whileで囲む)
waitは通常、条件判定とセットで使います。
synchronized(lock) {
while (!condition) {
lock.wait();
}
}
理由:
- スプリアスウェイクアップ(通知なし復帰)があり得る
- notifyAll後に条件を満たしていない場合がある
❌ 間違い例:
if (!condition) {
lock.wait();
}
これでは安全ではありません。
⚠ よくある失敗
- waitとnotifyを別のオブジェクトに対して呼ぶ
- notify後すぐ実行されると誤解する(ロック再取得が必要)
- notifyをsynchronized外で呼ぶ
- 条件変数を更新し忘れる
✔ 実装手順まとめ
- 共有ロックオブジェクトを作成
- synchronizedで囲む
- whileで条件判定
- waitで待機
- 別スレッドでnotify/notifyAll
- 条件を必ず更新する
3. waitとsleepの違い(検索意図の核心)
「java wait」で検索するユーザーの多くは、sleep() との違いを知りたいと考えています。
この2つは似ているように見えますが、設計思想も用途もまったく異なります。
3.1 決定的な違い(比較表)
| 項目 | wait | sleep |
|---|---|---|
| 所属クラス | Object | Thread |
| ロック解放 | する | しない |
| 使用場所 | synchronized内必須 | どこでも可 |
| 目的 | スレッド間通信 | 単純な時間停止 |
3.2 最大の違いは「ロックを解放するか」
✔ waitの動作
- 現在保持しているロックを解放する
- 他スレッドがロックを取得できる
- notifyで再開する
synchronized(lock) { lock.wait(); }
✔ sleepの動作
- ロックを保持したまま停止
- 他スレッドはロックを取得できない
Thread.sleep(1000);
⚠ ここが最重要ポイント
sleepは「時間待ち」
waitは「通知待ち」
3.3 実務での使い分け
✔ waitを使うケース
- Producer / Consumerパターン
- データ到着待ち
- 状態変更待ち
- スレッド間の協調制御
✔ sleepを使うケース
- 単純な遅延処理
- リトライ間隔調整
- テストコードでの待機
3.4 waitの時間指定
waitは時間指定も可能です。
lock.wait(1000); // 最大1秒待機
ただし注意点:
- 指定時間経過で復帰するが、条件が満たされている保証はない
- whileでの再判定が必須
⚠ 初心者が混乱するポイント
- sleepでも通知で復帰すると思っている → ❌
- waitは時間で必ず再開すると誤解 → ❌
- sleep中にロックが解放されると思っている → ❌
✔ 結論整理
- sleepは単なる時間停止
- waitはスレッド間通信の仕組み
- ロック解放の有無が最大の違い
4. waitの応用知識(安全に使うための必須理解)
waitは単純に見えて、誤用すると不具合を生みやすいメソッドです。
ここでは、安全に実装するために必須の知識を解説します。
4.1 タイムアウト付きwait
waitには時間指定版があります。
synchronized(lock) {
lock.wait(1000); // 最大1秒待機
}
動作仕様:
- 最大指定時間だけ待機
- notifyがあれば即復帰
- 時間経過でも復帰する
⚠ 重要
時間経過で復帰しても「条件が成立した」とは限りません。
必ず条件チェックを組み合わせます。
4.2 スプリアスウェイクアップとは
スプリアスウェイクアップ(Spurious Wakeup)とは、
通知なしでwaitが復帰する可能性がある現象です。
Java仕様上、これは起こり得るとされています。
つまり、
- notifyされていない
- 条件が変わっていない
- それでも復帰する可能性がある
このため、ifではなくwhileが必須になります。

4.3 正しいパターン(whileで囲む)
正しい実装例:
synchronized(lock) {
while (!condition) {
lock.wait();
}
// 条件成立後の処理
}
理由:
- スプリアスウェイクアップ対策
- notifyAll後に条件未成立の可能性
- 複数スレッド待機時の安全確保
❌ 危険な例:
synchronized(lock) {
if (!condition) {
lock.wait(); // 危険
}
}
この書き方では、誤動作する可能性があります。
4.4 デッドロックとの関係
waitはロックを解放しますが、
複数ロックを扱う場合はデッドロックが発生します。
例:
- スレッドA → lock1 → lock2
- スレッドB → lock2 → lock1
対策:
- ロック取得順序を統一
- ロック数を最小限にする
⚠ よくある致命的ミス
- 条件変数を更新しない
- notifyだけして状態変更しない
- synchronizedをネストしすぎる
- 複数ロックで順序を揃えない
✔ 応用理解まとめ
- waitはwhileで囲むのが絶対原則
- タイムアウトでも条件再判定必須
- ロック設計を誤るとデッドロック発生
5. よくあるエラーと解決方法
waitは低レベルAPIのため、実装ミスがそのまま不具合に直結します。
ここでは、実際によく発生するエラーと原因・対処法を整理します。
5.1 IllegalMonitorStateException
最も多いエラーです。
発生条件:
- synchronized外でwaitを呼んだ
- synchronized外でnotifyを呼んだ
❌ 誤り例
Object lock = new Object();
lock.wait(); // 例外発生
✔ 正しい例
synchronized(lock) {
lock.wait();
}
対策まとめ:
- wait / notifyは必ず同じオブジェクトのsynchronized内で呼ぶ
- ロック対象オブジェクトを統一する
5.2 notifyしても再開しない
原因パターン:
- 別オブジェクトに対してnotifyしている
- 条件変数が更新されていない
- notify後にロックを保持し続けている
❌ よくあるミス
synchronized(lock1) {
lock1.wait();
}
synchronized(lock2) {
lock2.notify(); // 別オブジェクト
}
✔ 必ず同一オブジェクトを使用する
synchronized(lock) {
lock.wait();
}
synchronized(lock) {
condition = true;
lock.notifyAll();
}
5.3 waitが永遠に終わらない
主な原因:
- notifyが一度も呼ばれていない
- 条件判定ロジックの誤り
- while条件が永遠にtrue
デバッグ手順:
- notifyが確実に呼ばれているか確認
- 条件変数が更新されているか確認
- ログ出力でスレッドの動作順を追う
5.4 デッドロック
典型例:
synchronized(lock1) {
synchronized(lock2) {
...
}
}
別スレッドで順序が逆だと停止します。
対策:
- ロック取得順序を統一
- 可能なら単一ロックにする
- java.util.concurrentの利用を検討
5.5 InterruptedExceptionの扱い
waitは InterruptedException をスローします。
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
推奨対応:
- 割り込みフラグを再設定する(interrupt再呼出)
- 例外を握りつぶさない
⚠ 実務で特に多いミス
- ifでwaitを囲む
- notifyを条件更新前に呼ぶ
- 複数ロック管理を曖昧にする
- 例外を空catchにする
✔ エラー回避の基本原則
- synchronized必須
- 同一オブジェクト使用
- whileパターン徹底
- notify前に状態更新
- interruptを適切に処理
6. 現代Javaでの代替手段(実務視点)
wait / notify はJava初期から存在する低レベルのスレッド制御手段です。
しかし現在の実務では、より安全で高水準なAPIを使うことが一般的です。
この章では、なぜ代替手段が推奨されるのかを整理します。
6.1 wait/notifyは低レベルAPI
waitを使う場合、開発者がすべてを手動管理します。
管理対象:
- ロック制御
- 条件変数
- 通知順序
- スプリアスウェイクアップ対策
- デッドロック回避
- 割り込み処理
つまり、バグの温床になりやすい設計です。
特に複数条件・複数スレッドが絡むと急激に複雑化します。
6.2 代表的な代替クラス
Java5以降は java.util.concurrent パッケージが提供されています。
✔ BlockingQueue
スレッド間データ受け渡し用。
BlockingQueue<String> queue = new LinkedBlockingQueue<>();
queue.put("data"); // 自動で待機制御
queue.take(); // データが来るまで待つ
特徴:
- wait/notify不要
- デッドロックが起きにくい
- 実務で非常に多用される
.
✔ CountDownLatch
複数スレッドの完了待ち。
CountDownLatch latch = new CountDownLatch(1);
latch.await(); // 待機
latch.countDown(); // 通知
用途:
- 初期化完了待ち
- 並列処理の同期
✔ ReentrantLock + Condition
waitに近い仕組みを、より明示的に扱えるAPI。
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock();
try {
condition.await();
} finally {
lock.unlock();
}
利点:
- 複数条件を管理可能
- ロック制御が柔軟
6.3 実務では何を使うべきか
状況別の推奨:
| 目的 | 推奨手段 |
|---|---|
| データ受け渡し | BlockingQueue |
| 単純同期 | CountDownLatch |
| 複雑条件制御 | ReentrantLock + Condition |
| 学習目的 | wait理解必須 |
結論:
- 学習段階ではwaitを理解することは重要
- 実務ではconcurrentパッケージを優先
⚠ 現場での判断基準
- スレッド制御を自作する必要があるか?
- 標準ライブラリで代替できないか?
- 可読性・保守性を確保できるか?
多くの場合、waitは使わなくても解決できます。
✔ 本章まとめ
- waitは低レベルAPI
- concurrentパッケージが現代標準
- 実務では安全性を優先する
7. まとめ(初心者向け最終整理)
java wait は、スレッド間で状態変化を待つための仕組みです。
単なる時間停止ではなく、「通知を待つ制御メカニズム」である点が最重要です。
ここまでの内容を、実装視点で整理します。
7.1 waitの本質
- Objectクラスのメソッド
- synchronized内でのみ使用可能
- 呼び出すとロックを解放して待機する
- notify / notifyAllで再開する
7.2 安全に書くための3原則
① synchronized必須
② whileで囲む
③ notify前に状態更新
正しいテンプレート:
synchronized(lock) {
while (!condition) {
lock.wait();
}
}
通知側:
synchronized(lock) {
condition = true;
lock.notifyAll();
}
7.3 sleepとの決定的な違い
| 項目 | wait | sleep |
|---|---|---|
| ロック解放 | する | しない |
| 用途 | スレッド間通信 | 時間待機 |
混同しないことが重要です。
7.4 実務での立ち位置
- 理解しておくことは必須
- しかし実務では concurrent パッケージを優先
- 自前のwait制御は慎重に設計する
7.5 最終チェックリスト
- 同一オブジェクトを使っているか
- synchronized内で呼んでいるか
- whileで囲んでいるか
- notify前に状態更新しているか
- interrupt処理をしているか
これらを守れば、重大な不具合は大Rememberに減らせます。
よくある質問
Q1. waitはThreadクラスのメソッドですか?
いいえ。Objectクラスのメソッドです。
Q2. synchronizedなしでwaitは使えますか?
使えません。IllegalMonitorStateExceptionが発生します。
Q3. notifyとnotifyAllはどちらを使うべきですか?
安全性重視ならnotifyAllを推奨します。
Q4. waitは時間指定できますか?
可能です。wait(long millis)を使用します。
Q5. ifではなくwhileを使う理由は?
スプリアスウェイクアップや条件未成立復帰を防ぐためです。
Q6. 実務でwaitはよく使われますか?
近年はjava.util.concurrentの利用が主流です。
Q7. sleepで代用できますか?
できません。sleepはロックを解放しません。

