欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java多線程中sleep和wait區(qū)別

 更新時間:2023年06月06日 10:54:15   作者:逆流°只是風(fēng)景-bjhxcc  
本文主要介紹了Java多線程中sleep和wait區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

sleep(休眠) 和 wait(等待) 方法是 Java 多線程中常用的兩個方法,它們有什么區(qū)別及一些該注意的地方有哪些呢?下面給大家一一分解。

sleep和wait方法都是native關(guān)鍵字修飾的方法,這說明這兩個方法是原生函數(shù),也就是由C/C++實現(xiàn)的,那么我們就暫時不關(guān)心它的具體實現(xiàn)了。

sleep方法是Thread類中的方法,而wait方法是Object中的方法,那么我們首先看看wait方法。

wait()

從Object源碼中,我們可以發(fā)現(xiàn),wait有三個重載方法,分別是無參的wait方法,帶有l(wèi)ong和int類型參數(shù)的的wait方法,以及帶有l(wèi)ong類型參數(shù)的方法。其實前兩個方法最終都是調(diào)用了wait(long)方法,而wait(long)方法是native修飾的方法,它底層就是由C++實現(xiàn)的,這里暫且不討論,我們來看看這個方法的注釋。

/*
wait方法會導(dǎo)致當(dāng)前線程等待,直到其他線程調(diào)用notify和notifyAll方法,或者達(dá)到了指定的等待時間
使用wait方法的前提是當(dāng)前線程擁有該對象的監(jiān)視器也就是鎖
該方法會導(dǎo)致當(dāng)前線程T(調(diào)用wait方法的線程)將自己放到該對象的等待集合中,然后會放棄此對象上的所有同步聲明,也就是會放棄對象的鎖
該等待線程不會被調(diào)度并且處于休眠狀態(tài),直到以下四種情況之一發(fā)生:
1、其他線程調(diào)用等待對象notify方法,并且當(dāng)前線程T被隨機選為要喚醒的方法時,線程將會退出休眠狀態(tài)
2、其他線程調(diào)用等待對象的notifyAll方法
3、其他線程中斷當(dāng)前線程T
4、超過指定的等待時間。如果等待時間為0的話,時間因素將不會被考慮,那線程將等待直到被通知喚醒
當(dāng)發(fā)生上述四種情況時,線程T將會從該對象的等待集合中移除,并且可以重新被調(diào)度。然后它以通常的方式與其他線程競爭對象上的同步鎖,一旦它獲得了對對象的控制權(quán),它對對象的所有同步聲明將會恢復(fù)到原來的狀態(tài),也就是說,恢復(fù)到調(diào)用wait方法時的狀態(tài)。然后線程T將會從調(diào)用wait方法的方法中返回。因此,從wait方法返回時,對象和線程T的同步狀態(tài)與調(diào)用wait方法時的狀態(tài)完全相同。
如果當(dāng)前線程在等待之前或者等待時被中斷,會拋出InterruptedException異常。
注意,等待方法將當(dāng)前線程防止到該對象的等待集合時,只解鎖此對象;在線程等待時,當(dāng)前線程同步的其他任何對象都將保持鎖定狀態(tài)。
wait方法僅能被持有對象監(jiān)視器的線程調(diào)用(對象監(jiān)視器就相當(dāng)于對象的鎖)
通過如下方法可以獲得對象的監(jiān)視器:
1、通過執(zhí)行該對象的同步方法(也就是synchronized關(guān)鍵字修飾的方法)
2、通過執(zhí)行該對象的同步代碼塊(synchronized(Object) {})
3、通過執(zhí)行類的同步靜態(tài)代碼塊(也就是synchronized關(guān)鍵字修飾的靜態(tài)方法)
*/
public final native void wait(long timeout) throws InterruptedException;

 通過wait方法的注釋,我們可以發(fā)現(xiàn),wait方法有如下作用:

  • 使線程進入休眠狀態(tài),不被調(diào)度,直到被notify方法選中或者notifyAll方法的執(zhí)行,才會被喚醒
  • 線程會釋放調(diào)用wait方法的對象的鎖(但是不會釋放線程持有的其他對象的鎖),這樣其他線程可以競爭該對象的鎖
  • 從wait方法中退出后,線程會回到調(diào)用該方法時的狀態(tài)

既然要notify和notifyAll方法才能喚醒調(diào)用wait方法陷入等待的線程,那么我們看看這兩個方法的注釋:

/*
喚醒一個等待對象鎖的線程
如果有多個線程在等待該對象,會隨機喚醒一個線程
在當(dāng)前線程放棄該對象的鎖之前,喚醒的線程將無法繼續(xù)執(zhí)行
喚醒的線程將以通常的方式與其他線程競爭,這些線程會公平地在這個對象上進行同步競爭。例如,被喚醒的線程在競爭對象的鎖時沒有特權(quán)或者缺點
該方法僅在線程持有對象的監(jiān)視器時才能被調(diào)用,獲取對象的監(jiān)視器有如下方法:也就是synchronized關(guān)鍵字修飾的方法、靜態(tài)方法以及代碼塊等
*/
public final native void notify();
/*
喚醒所有等待該對象監(jiān)視器的線程
在當(dāng)前持有對象鎖的線程放棄對象鎖之前,被喚醒的線程無法執(zhí)行
*/
public final native void notifyAll();

通過方法的注釋來看,這兩個方法就是用于喚醒等待該對象的線程,notify隨機喚醒一個線程,notifyAll會喚醒全部線程,這些被喚醒的線程處于就緒態(tài),它們會和正在運行的線程一起搶占對象的鎖,得到對象的鎖之后才能繼續(xù)執(zhí)行。

通過JDK源碼的注釋,我們對wait和notify方法有了更進一步的了解,那么接著看看sleep方法,才能知道wait和sleep的區(qū)別

sleep()

Thread類中的sleep方法也有兩個重載方法,其中sleep(long)是底層的實現(xiàn)。

來看看它們的注釋:

/*
根據(jù)系統(tǒng)計時器和調(diào)度程序的精度和準(zhǔn)確性,使當(dāng)前執(zhí)行的線程休眠(暫時停止執(zhí)行)指定的毫秒數(shù)
線程不會釋放持有的鎖
該方法響應(yīng)中斷,當(dāng)遇到中斷時會拋出InterruptedException異常,并且會清除當(dāng)前線程的中斷狀態(tài)
*/
public static native void sleep(long millis) throws InterruptedException;
/*
使當(dāng)前執(zhí)行的線程休眠(臨時停止執(zhí)行)指定的毫秒數(shù)加上指定的納秒數(shù),具體取決于系統(tǒng)計時器和調(diào)度程序的精度和準(zhǔn)確性
線程不會釋放持有的鎖
nanos的范圍:0-999999
*/
public static void sleep(long millis, int nanos)
? ? throws InterruptedException {
? ? ?? ?// 判斷millis和nanos是否符合條件
? ? ? ? if (millis < 0) {
? ? ? ? ? ? throw new IllegalArgumentException("timeout value is negative");
? ? ? ? }
? ? ? ? if (nanos < 0 || nanos > 999999) {
? ? ? ? ? ? throw new IllegalArgumentException(
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "nanosecond timeout value out of range");
? ? ? ? }
? ? ? ? if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
? ? ? ? ? ? millis++;
? ? ? ? }
?? ??? ?// 調(diào)用原生sleep方法
? ? ? ? sleep(millis);
? ? }

wait()和sleep()方法區(qū)別

區(qū)別1:使用限制

使用 sleep 方法可以讓讓當(dāng)前線程休眠,時間一到當(dāng)前線程繼續(xù)往下執(zhí)行,在任何地方都能使用,但需要捕獲 InterruptedException 異常。

try {
    Thread.sleep(3000L);
} catch (InterruptedException e) {
    e.printStackTrace();
}

而使用 wait 方法則必須放在 synchronized 塊里面,同樣需要捕獲 InterruptedException 異常,并且需要獲取對象的鎖。

synchronized (lock){
    try {
        lock.wait();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

而且 wait 還需要額外的方法 notify/ notifyAll 進行喚醒,它們同樣需要放在 synchronized 塊里面,且獲取對象的鎖。。

synchronized (lock) {
? ? // 隨機喚醒
? ? lock.notify();
? ? // 喚醒全部
? ? lock.notifyAll();
}

當(dāng)然也可以使用帶時間的 wait(long millis) 方法,時間一到,無需其他線程喚醒,也會重新競爭獲取對象的鎖繼續(xù)執(zhí)行。

區(qū)別2:使用場景

sleep 一般用于當(dāng)前線程休眠,或者輪循暫停操作,wait 則多用于多線程之間的通信。

區(qū)別3:所屬類

sleep 是 Thread 類的靜態(tài)本地方法,wait 則是 Object 類的本地方法。

java.lang.Thread#sleep

public static native void sleep(long millis) throws InterruptedException;

java.lang.Object#wait

public final native void wait(long timeout) throws InterruptedException;

為什么要這樣設(shè)計呢?

因為 sleep 是讓當(dāng)前線程休眠,不涉及到對象類,也不需要獲得對象的鎖,所以是線程類的方法。wait 是讓獲得對象鎖的線程實現(xiàn)等待,前提是要楚獲得對象的鎖,所以是類的方法。

區(qū)別4:釋放鎖

Object lock = new Object();
synchronized (lock) {
    try {
        lock.wait(3000L);
        Thread.sleep(2000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

如上代碼所示,wait 可以釋放當(dāng)前線程對 lock 對象鎖的持有,而 sleep 則不會。

區(qū)別5:線程切換

sleep 會讓出 CPU 執(zhí)行時間且強制上下文切換,而 wait 則不一定,wait 后可能還是有機會重新競爭到鎖繼續(xù)執(zhí)行的。

到此這篇關(guān)于Java多線程中sleep和 wait區(qū)別的文章就介紹到這了,更多相關(guān)Java sleep和wait內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論