sleep()是“霸座睡觉”,wait()是“让座等叫号”?这比喻绝了,Java并发不再难!
故事场景:繁忙都市中的“共享资源休息室”
在繁华的都市中心,有一间特殊的“共享资源休息室”。这个休息室里有一些非常抢手的“独享沙发”(同步锁/对象监视器 synchronized(lockObject)
)。许多访客(线程 Thread
)都想进来使用这些独享沙发。
休息室有两位管理员,负责管理访客的“暂停”行为:
-
1. “精准计时员”
Thread.sleep()
先生:他负责让访客在原地“强制小睡”一段时间。 -
2. “呼叫等待员”
Object.wait()
女士:她负责让访客在拿到“独享沙发”后,如果发现某些条件还不满足,可以暂时“交出沙发并去等候区等待叫号”。
“精准计时员” Thread.sleep()
先生 - 强制小睡,沙发不放
// 代码片段回顾 (Thread.sleep()):
// Thread.sleep(3000); // 当前线程原地小睡3秒
//
// synchronized(lock) {
// Thread.sleep(2000); // 即使在同步块里睡,这个线程依然霸占着 lock 这个“独享沙发”
// }
故事解读:
Thread.sleep()
先生,是一位非常守时的管理员。当一位访客(当前线程)找到他,说:“我想在这里眯一会儿,比如3秒钟。”
-
• 他的行动:
sleep()
先生会立刻对这位访客施加一个“强制小睡魔法”。这位访客会立即在原地进入睡眠状态,精确到毫秒。 -
• 关键特点:沙发不离身!
-
• 如果这位访客在小睡前并没有坐上任何“独享沙发”(即没有持有任何锁),那他就在自己的角落里睡,不影响任何人。
-
• 但是! 如果这位访客在小睡前已经抢到了一个“独享沙发”(即他当前正持有一个或多个锁,比如在一个
synchronized
块内部调用了sleep()
),那么在整个小睡期间,他依然会死死地霸占着这个沙发,绝对不会松手! 其他想用这个沙发的访客只能在外面干等着,直到他睡醒并且主动离开沙发(退出synchronized
块)。
-
-
• 如何醒来:
-
1. 时间到了:设定的睡眠时间结束,访客自然醒来,继续他之前未完成的事情。
-
2. 被强行叫醒:如果有人(其他线程)非常粗鲁地打断了他的美梦(调用了该线程的
interrupt()
方法),他会惊醒并抛出一个“被打扰了!”的抱怨(InterruptedException
)。
-
-
• 所属部门:
sleep()
是Thread
类的静态方法,意思是这是每个线程自带的“自我催眠”技能,不需要依赖某个特定的“沙发”对象。
Thread.sleep()
先生的风格:“时间到!睡X秒!我睡我的,我的沙发(锁)谁也别想碰!”
“呼叫等待员” Object.wait()
女士 - 交出沙发,等待叫号
// 代码片段回顾 (Object.wait() / notify()):
// synchronized (lock) { // 必须先抢到 lock 这个“独享沙发”
// System.out.println("我抢到沙发了,但发现茶水还没来...");
// lock.wait(); // “呼叫等待员”介入:交出沙发,去等候区等茶水(等待被 notify)
// System.out.println("茶水来了!我被叫回来了,重新抢回了沙发!");
// }
//
// synchronized (lock) { // 另一个线程,比如送茶水的服务员
// System.out.println("茶水准备好了!");
// lock.notify(); // 服务员大喊一声:“等茶水的客人,茶水好啦!” (通知一个等待者)
// // 或者 lock.notifyAll(); // 通知所有等茶水的客人
// }
故事解读:
Object.wait()
女士,是一位负责协调“资源等待”的管理员。她的工作场景比较特殊,必须发生在“独享沙发”区域(即必须在 synchronized (