天貓二面: Wait 和 Sleep 有什么區(qū)別?
作為一名 Java 開(kāi)發(fā)者,尤其是涉及到多線程的部分,wait 和 sleep 是兩個(gè)經(jīng)常會(huì)碰到的方法。雖然它們看起來(lái)功能相似,但實(shí)際上有著顯著的區(qū)別。這篇文章,我們將深入探討兩者的不同之處,并理解它們背后的原理。
一、wait 與 sleep 的基本區(qū)別
首先,讓我們來(lái)看看 wait 和 sleep 的幾個(gè)主要區(qū)別:
(1) 所屬類不同
- wait():是 java.lang.Object 類的方法。這意味著每一個(gè)對(duì)象都能調(diào)用 wait() 方法。
- sleep():是 java.lang.Thread 類的靜態(tài)方法。它屬于線程類本身,不依賴于具體的對(duì)象。
(2) 鎖的持有情況
- wait():只能在同步方法或同步代碼塊中調(diào)用,必須持有對(duì)象的鎖。調(diào)用 wait() 后,線程會(huì)釋放鎖。
- sleep():可以在任何地方調(diào)用,不需要持有任何鎖。調(diào)用 sleep() 時(shí),線程不會(huì)釋放它持有的鎖。
(3) 主要用途
- wait():主要用于線程之間的通信和協(xié)調(diào),通常與 notify() 或 notifyAll() 一起使用,實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者模式等。
- sleep():主要用于讓線程暫停執(zhí)行一段時(shí)間,通常用于控制執(zhí)行速度或?qū)崿F(xiàn)延時(shí)操作。
(4) 鎖的釋放
- wait():調(diào)用后會(huì)釋放對(duì)象的監(jiān)視器鎖,讓其他等待該鎖的線程有機(jī)會(huì)執(zhí)行。
- sleep():調(diào)用后不會(huì)釋放任何鎖,線程在睡眠期間仍然持有所有已經(jīng)獲取的鎖。
二、原理分析
理解 wait 和 sleep 的原理,有助于更好地掌握它們的使用場(chǎng)景。
1. wait() 的原理
wait() 方法使當(dāng)前線程進(jìn)入等待狀態(tài),直到被其他線程通過(guò) notify() 或 notifyAll() 喚醒,或者等待時(shí)間超過(guò)指定的超時(shí)時(shí)間。調(diào)用 wait() 前,線程必須持有對(duì)象的監(jiān)視器鎖。調(diào)用后,線程會(huì)釋放該鎖,允許其他線程進(jìn)入同步塊或方法執(zhí)行。當(dāng)被喚醒后,線程會(huì)重新競(jìng)爭(zhēng)鎖,有機(jī)會(huì)繼續(xù)執(zhí)行。
這種機(jī)制主要用于線程間的協(xié)作。例如,生產(chǎn)者線程生產(chǎn)數(shù)據(jù)后通過(guò) notify() 喚醒消費(fèi)者線程進(jìn)行消費(fèi)。
2. sleep() 的原理
sleep() 方法讓當(dāng)前線程暫停執(zhí)行指定的時(shí)間,但線程在此期間不會(huì)釋放任何鎖。調(diào)用 sleep() 后,線程進(jìn)入休眠狀態(tài),時(shí)間到達(dá)后自動(dòng)恢復(fù)運(yùn)行。這種方式主要用于控制線程的執(zhí)行節(jié)奏,比如定時(shí)任務(wù)或模擬延時(shí)操作。
sleep() 不依賴于同步機(jī)制,因此更加靈活,但也不適合用于線程間的協(xié)作。
三、實(shí)例演示
讓我們通過(guò)兩個(gè)簡(jiǎn)單的示例,看看 wait() 和 sleep() 在實(shí)際中的使用區(qū)別。
1. 使用 wait()
假設(shè)我們有一個(gè)共享資源,生產(chǎn)者線程負(fù)責(zé)生成數(shù)據(jù),消費(fèi)者線程負(fù)責(zé)消費(fèi)數(shù)據(jù)。我們希望在沒(méi)有數(shù)據(jù)時(shí),消費(fèi)者線程等待;有數(shù)據(jù)時(shí),消費(fèi)者被喚醒并消費(fèi)數(shù)據(jù)。
public class ProducerConsumer {
privatefinal Object lock = new Object();
privateboolean hasData = false;
public void produce() throws InterruptedException {
synchronized (lock) {
while (hasData) {
lock.wait(); // 等待消費(fèi)者消費(fèi)數(shù)據(jù)
}
System.out.println("生產(chǎn)數(shù)據(jù)...");
hasData = true;
lock.notify(); // 喚醒消費(fèi)者
}
}
public void consume() throws InterruptedException {
synchronized (lock) {
while (!hasData) {
lock.wait(); // 等待生產(chǎn)者生產(chǎn)數(shù)據(jù)
}
System.out.println("消費(fèi)數(shù)據(jù)...");
hasData = false;
lock.notify(); // 喚醒生產(chǎn)者
}
}
public static void main(String[] args) {
ProducerConsumer pc = new ProducerConsumer();
// 生產(chǎn)者線程
new Thread(() -> {
try {
while (true) {
pc.produce();
Thread.sleep(1000); // 模擬生產(chǎn)時(shí)間
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Producer").start();
// 消費(fèi)者線程
new Thread(() -> {
try {
while (true) {
pc.consume();
Thread.sleep(1500); // 模擬消費(fèi)時(shí)間
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Consumer").start();
}
}
在這個(gè)例子中,produce() 和 consume() 方法都在同步塊中調(diào)用 wait() 和 notify(),實(shí)現(xiàn)了生產(chǎn)者和消費(fèi)者之間的協(xié)調(diào)。當(dāng)生產(chǎn)者生產(chǎn)完數(shù)據(jù)后,通過(guò) notify() 喚醒等待的消費(fèi)者;消費(fèi)者消費(fèi)完數(shù)據(jù)后,喚醒生產(chǎn)者繼續(xù)生產(chǎn)。
2. 使用 sleep()
現(xiàn)在,我們來(lái)看一個(gè)簡(jiǎn)單的使用 sleep() 的例子,來(lái)模擬延時(shí)操作。
public class SleepExample {
public static void main(String[] args) {
System.out.println("任務(wù)開(kāi)始執(zhí)行...");
try {
Thread.sleep(2000); // 暫停2秒
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("任務(wù)執(zhí)行完畢!");
}
}
運(yùn)行這個(gè)程序,你會(huì)發(fā)現(xiàn)"任務(wù)開(kāi)始執(zhí)行..."輸出后,程序暫停了2秒,然后輸出"任務(wù)執(zhí)行完畢!"。這里,sleep() 簡(jiǎn)單地讓線程暫停了一段時(shí)間,而不涉及任何同步或線程間通信。
四、總結(jié)
本文,我們分析了 wait() 和 sleep() 的區(qū)別,通過(guò)今天的分享,我們了解了 wait() 和 sleep() 在 Java 中的區(qū)別和各自的使用場(chǎng)景:
- wait() 是用于線程間的協(xié)調(diào)與通信,必須在同步環(huán)境下使用,并且會(huì)釋放鎖。
- sleep() 用于讓線程暫停執(zhí)行一段時(shí)間,不涉及鎖的釋放,適用于延時(shí)操作。
掌握這兩個(gè)方法的區(qū)別和用法,能夠幫助我們更有效地管理多線程環(huán)境下的線程行為,提高程序的并發(fā)性能和穩(wěn)定性。