sleep和wait詳解與區別

0.sleep和wait的區別

sleep()是使線程暫停執行一段時間的方法。wait()也是一種使線程暫停執行的方法。例如,當線程執行wait()方法時候,會釋放當前的鎖,然後讓出CPU的執行權,進入等待狀態。並且可以調用notify()方法或者notifyAll()方法通知正在等待的其他線程。notify()方法僅喚醒一個線程(等待隊列中的第一個線程)並允許他去獲得鎖。notifyAll()方法喚醒所有等待這個對象的線程並允許他們去競爭獲得鎖。具體區別如下:

1.原理不同。sleep()方法是Thread類的靜態方法,是線程用來控制自身流程的,他會使此線程暫停執行一段時間,而把執行機會讓給其他線程,等到計時時間一到,此線程會自動甦醒。例如,當線程執行報時功能時,每一秒鐘打印出一個時間,那麼此時就需要在打印方法前面加一個sleep()方法,以便讓自己每隔一秒執行一次,該過程如同鬧鐘一樣。而wait()方法是Object類的方法,用於線程間通信,這個方法會使當前擁有該對象鎖的進程等待,直到其他線程調用notify()方法或者notifyAll()時才醒來,不過開發人員也可以給他指定一個時間,自動醒來。

2.對鎖的 處理機制不同。由於sleep()方法的主要作用是讓線程暫停執行一段時間,時間一到則自動恢復,不涉及線程間的通信,因此,調用sleep()方法並不會釋放鎖。而wait()方法則不同,當調用wait()方法後,線程會釋放掉他所佔用的鎖,從而使線程所在對象中的其他synchronized數據可以被其他線程使用。

3.使用區域不同。wait()方法必須放在同步控制方法和同步代碼塊中使用,sleep()方法則可以放在任何地方使用。sleep()方法必須捕獲異常,而wait()notify()notifyAll()不需要捕獲異常。在sleep的過程中,有可能被其他對象調用他的interrupt(),產生InterruptedException。由於sleep不會釋放鎖標誌,容易導致死鎖問題的發生,因此一般情況下,推薦使用wait()方法。

1.wait方法

wait方法是Object提供方法,可通過wait方法讓當前線程進入等待。

1.1 先看看wait方法的介紹。

通過查閱JDK1.8文檔發現Object中的wait方法有三個重載方法。
分別爲:

  • void wait()
    導致當前線程等待,直到另一個線程爲此對象調用notify()方法或notifyAll()方法。實際上wait()方法是僅僅是調用wait(0L)

  • void wait(long timeout)
    導致當前線程等待,直到另一個線程調用此對象的notify()方法或notifyAll()方法,或者已經過了指定的時間量,如果timeout爲0L那麼將爲永久等待相當於調用wait()

  • void wait(long timeout, int nanos)
    導致當前線程等待,直到另一個線程爲此對象調用notify()方法或notifyAll()方法,或者某個其他線程中斷當前線程,或者已經過了一定量的實時。

1.2 wait方法的特點

1.wait方法只能在synchronized代碼塊內調用,否則會拋出IllegalMonitorStateException 異常。也就是說調用wait方法的線程必須持有鎖。

示例代碼

package spring.cache.jvm;

public class Demo implements Runnable{

    @Override
    public void run() {
        
        System.out.println("start");
        long start = System.currentTimeMillis();

        try {
            this.wait(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long end = System.currentTimeMillis();
        System.out.println("end");
        System.out.println(end-start);
        
    }
    public static void main(String[] args) {
        Demo demo = new Demo();
        Thread thread1 = new Thread(demo);
        thread1.start();
    }
}

控制檯

start
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
	at java.lang.Object.wait(Native Method)
	at spring.cache.jvm.Demo.run(Demo.java:25)
	at java.lang.Thread.run(Thread.java:748)

進程完成,退出碼 0

2.線程調用wait方法時該線程釋放此鎖(監視器)的所有權並進入等待狀態,並把CPU的執行權讓給其他線程,直到其他線程調用notify方法或notifyAll方法喚醒。

public class Demo implements Runnable{

    @Override
    public void run() {

        System.out.println("start");
        synchronized (this){
            try {
                System.out.println("當前線程名字爲:"
                +Thread.currentThread().getName());
                //喚醒其它正在等待的線程
                this.notify();
                //導致線程等待,並釋放鎖。
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("end");
    }
    
    public static void main(String[] args) {
        Demo demo = new Demo();
        Thread thread1 = new Thread(demo);
        thread1.start();

        Thread thread2 = new Thread(demo);
        thread2.start();
    }
}

控制檯

start
start
當前線程名字爲:Thread-0
當前線程名字爲:Thread-1
end

1.3 wait方法實現

1.wait() 源碼

 public final void wait() throws InterruptedException {
        this.wait(0L);
 }

wait()方法其實是調用wait(long timeout) 方法並傳入參數0L

2.wait(long timeout) 源碼

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

wait(long timeout) 方法被native修飾了,也就是說他不是java方法,他是一個本地方法,底層實現的可能是C和C++。

3.wait(long timeout, int nanos)源碼

 public final void wait(long timeout, int nanos) throws InterruptedException {
  	if (timeout < 0L) {//timeout小於0則報錯
        throw new IllegalArgumentException("timeout value is negative");
    } else if (nanos >= 0 && nanos <= 999999) {//nanos 大於等於 0 && 小於等於 999999
        if (nanos > 0) { //nanos 大於 0
            ++timeout; //timeout自增1
        }
        this.wait(timeout); //調用wait(long timeout)
    } else { 
        throw new IllegalArgumentException("nanosecond timeout value out of range");
    }
}

wait(long timeout, int nanos)文檔中的介紹解釋的不是很清楚,通過翻閱源碼發現其實wait(long timeout, int nanos)底層也是調用wait(long timeout) 方法,只不過做了些處理,如果nanos有值的話,並且nanos大於0,則timeout加1。舉個例子:例如this.wait(1000,999)那麼這時候線程實際等待時間爲1001

2.sleep方法

sleepThread對象提供的方法,可通過sleep方法讓當前線程進入休眠。

2.1 先看看sleep方法的介紹。

通過查閱JDK1.8文檔發現Thread中的sleep方法有兩個重載方法。

  • static void sleep(long timeout)
    使當前正在執行的線程以指定的毫秒數暫停(暫時停止執行),具體取決於系統定時器和調度程序的精度和準確性。

  • static void sleep(long timeout, int nanos)
    導致正在執行的線程以指定的毫秒數加上指定的納秒數來暫停(臨時停止執行),這取決於系統定時器和調度器的精度和準確性。

2.2 sleep方法的特點

1.sleep可以在synchronized代碼塊內調用,也就是說調用sleep的線程不用持有鎖。

示例代碼

package spring.cache.jvm;

public class Demo implements Runnable{

    @Override
    public void run() {

        long start = System.currentTimeMillis();
        try {
            //讓線程休眠1秒
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("執行時間:"+(end-start));
        
    }
    public static void main(String[] args) {

        Demo demo = new Demo();

        Thread thread = new Thread(demo);
        thread.start();
    }
}

控制檯

執行時間:1000
進程完成,退出碼 0

2.sleep方法讓線程休眠並不會釋放鎖,但會交出CPU的執行權。

示例代碼

package spring.cache.jvm;

public class Demo implements Runnable{

    @Override
    public void run() {

        long start = System.currentTimeMillis();
        synchronized (this){
            try {
                //讓線程休眠1秒
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        long end = System.currentTimeMillis();
        System.out.println(Thread.currentThread().getName()+"的執行時間:"
        +(end-start));

    }
    public static void main(String[] args) {

        Demo demo = new Demo();

        Thread thread = new Thread(demo);
        thread.start();

        Thread thread1 = new Thread(demo);
        thread1.start();
    }
}

控制檯

Thread-0的執行時間:1000
Thread-1的執行時間:2000
進程完成,退出碼 0

1.線程Thread-0進入synchronized代碼塊,得到同步鎖並進入休眠1秒,Thread-1嘗試獲取鎖,獲取不到(鎖此時被Thread-0佔用)。
2.Thread-0喚醒出synchronized代碼塊釋放鎖並打印,Thread-1獲取到鎖進行等待1秒後打印。

2.sleep方法讓線程休眠並不會釋放鎖,但會交出CPU的執行權。

2.3 sleep方法實現

1.sleep(long timeout) 源碼

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

sleep(long timeout) 方法被native修飾了,也就是說他不是java方法,他是一個本地方法,底層實現的可能是C和C++。

2.sleep(long timeout, int nanos)源碼

public static void sleep(long timeout, int nanos) throws InterruptedException {
   if (timeout< 0L) { //過期時間小於0L 拋出 超時值爲負 異常。
       throw new IllegalArgumentException("timeout value is negative");
   } else if (nanos>= 0 && nanos<= 999999) {//nanos 大於等於 0 && 小於等於 999999
       if (nanos >= 500000 || (nanos!= 0 && timeout== 0L)) { // nanos 大於等於 500000  或者nanos 不等於 0 並且 timeout 等等於 0L
           ++timeout; //timeout自增
       }
       sleep(timeout);
   } else {
       throw new IllegalArgumentException("nanosecond timeout value out of range");
   }
    }

sleep(long timeout, int nanos)文檔中的介紹解釋的不是很清楚,通過翻閱源碼發現其實sleep(long timeout, int nanos)底層也是調用sleep(long timeout) 方法,只不過做了些處理,具體看上面源碼解讀。

3.sleep和wait方法區別總結

  • waitObject提供,sleepThread提供。
  • wait方法只能在synchronized代碼塊內調用,而sleep沒有做限制。
  • wait方法讓線程陷入等待/休眠階段時,會釋放同步鎖。而sleep並不會釋放同步鎖。
  • wait方法可以不指定時間戳(線程的休眠時間)將由notify()方法或notifyAll()方法喚醒。而sleep必需指定時間戳(線程的休眠時間)。

以上就是waitsleep的一個區別,有一篇文章講的還是蠻不錯的。如果大家看完還是不能夠很好總結可以參考一下這篇文章:
https://blog.csdn.net/qiuchaoxi/article/details/79837568

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章