
wait() 仅在另一线程已进入等待队列后调用 notify() 才有效;若 notify() 先于 wait() 执行(竞态),通知将丢失,导致永久阻塞——这是其设计本质决定的,非 bug,需用 semaphore、condition 或回调机制规避。
wait() 仅在另一线程已进入等待队列后调用 notify() 才有效;若 notify() 先于 wait() 执行(竞态),通知将丢失,导致永久阻塞——这是其设计本质决定的,非 bug,需用 semaphore、condition 或回调机制规避。
在 Java 并发编程中,Object.wait() / notify() 是基于 JVM 监视器(monitor)的底层协作原语,但其行为极易被误解,尤其在跨线程状态同步场景(如蓝牙开关监听)中,常出现“调用了 notify() 却唤不醒 wait() 线程”的问题。根本原因不在于代码写错,而在于对 wait()/notify() 的时序约束与语义边界缺乏深层理解。
? 核心前提:wait() 和 notify() 必须作用于同一对象 + 同一监视器队列
synchronized (lock) {
lock.wait(); // ✅ 正确:持有锁时调用
}
// ...
synchronized (lock) {
lock.notify(); // ✅ 正确:同一 lock 对象,且在 synchronized 块内
}
你的代码中虽满足该前提,但存在更关键的时序陷阱:
⚠️ 致命竞态:notify() 发生在 wait() 之前 → 通知丢失
观察你 enableBluetooth() 中的逻辑:
synchronized (lock) {
mBluetoothAdapter.enable(); // 异步开启蓝牙(非阻塞)
lock.wait(); // ❌ 当前线程在此挂起,释放 lock
}
而 Runnable 中的 notify() 是在 phandler.postDelayed(...) 循环中异步检测并触发的:
if (bluetoothAdapter.isEnabled()) {
synchronized (lock) {
lock.notify(); // ⚠️ 若此时主线程尚未执行到 lock.wait(),
// 或刚释放锁但还未进入 WaitSet,notify 将静默失败!
}
}
由于 mBluetoothAdapter.enable() 是异步操作,Runnable 可能在 wait() 调用前就完成首次检测(例如蓝牙本就处于开启中转态),导致 notify() 提前发出——而 wait() 还未开始等待,通知即告失效。这是 wait/notify 固有缺陷:它不保存通知信号,仅唤醒当前已在等待队列中的线程。
? 其他常见失效场景(对照排查)
| 线程状态 | 是否响应 notify()
|
原因说明 |
|---|---|---|
BLOCKED(争锁中) |
❌ 否 | 等待获取锁,与 monitor 队列无关 |
TIMED_WAITING(Thread.sleep()) |
❌ 否 | JVM 级睡眠,不关联任何对象锁 |
WAITING(LockSupport.park()) |
❌ 否 | 使用 AQS 队列,与 Object monitor 完全隔离 |
WAITING(其他对象上的 wait()) |
❌ 否 |
notify() 只影响目标锁对象的等待队列 |
✅ 唯一能被
lock.notify()唤醒的,是已调用lock.wait()、且成功进入该lock对象 WaitSet 的线程。
✅ 推荐解决方案(按优先级排序)
1. 使用 Semaphore —— 解决“通知丢失”最直接
Semaphore 内部维护计数器,可安全跨越竞态:
private final Semaphore bluetoothReady = new Semaphore(0); // 初始无许可
// 在 enableBluetooth() 中替换 wait:
try {
mBluetoothAdapter.enable();
bluetoothReady.acquire(); // 阻塞直到 acquire 到许可
isBluetoothOn = true;
onBluetoothConnected();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 在 Runnable 中替换 notify:
if (bluetoothAdapter.isEnabled()) {
bluetoothReady.release(); // ✅ 即使先调用,许可也会被暂存
handler.removeCallbacks(this);
}
2. 改用 Condition(配合 ReentrantLock)—— 更精细控制
private final ReentrantLock lock = new ReentrantLock();
private final Condition bluetoothOn = lock.newCondition();
// 等待端
lock.lock();
try {
while (!isBluetoothOn()) {
bluetoothOn.await(); // ✅ 自动重入检查,避免虚假唤醒
}
} finally {
lock.unlock();
}
// 通知端(在 Runnable 中)
lock.lock();
try {
if (bluetoothAdapter.isEnabled()) {
isBluetoothOn = true;
bluetoothOn.signal(); // ✅ 精准唤醒
}
} finally {
lock.unlock();
}
3. 彻底放弃阻塞式等待 → 回调 + 状态机(Android 推荐)
// 注册广播接收器监听蓝牙状态变更(更可靠、解耦)
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
context.registerReceiver(bluetoothStateReceiver, filter);
private final BroadcastReceiver bluetoothStateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
if (state == BluetoothAdapter.STATE_ON) {
isBluetoothOn = true;
onBluetoothConnected();
}
}
};
? 关键总结
-
wait()/notify()不是通用线程通信工具,而是严格配对的、低级的 monitor 协作协议,要求精确的时序和锁持有。 -
永远不要依赖
notify()的“即时性”:它不排队、不记忆、不补偿,只对“此刻正在等待”的线程生效。 - 在 Android 等异步系统服务场景中,优先采用
Semaphore、CountDownLatch、LiveData或系统级回调(如BroadcastReceiver),而非wait/notify。 - 若必须使用
wait/notify,务必确保:①notify()调用前,目标线程已wait();② 使用while循环重检条件(防虚假唤醒);③notify()和wait()在同一锁对象上。
真正的并发健壮性,不在于“如何唤醒”,而在于“如何设计不依赖唤醒的协作模型”。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











