一.查看API
sleep是Thread類的方法,導致此線程暫停執行指定時間,給其他線程執行機會,但是依然保持着監控狀態,過了指定時間會自動恢復,調用sleep方法不會釋放鎖對象。
當調用sleep方法后,當前線程進入阻塞狀態。目的是讓出CPU給其他線程運行的機會。但是由於sleep方法不會釋放鎖對象,所以在一個同步代碼塊中調用這個方法后,線程雖然休眠了,但其他線程無法訪問它的鎖對象。這是因為sleep方法擁有CPU的執行權,它可以自動醒來無需喚醒。而當sleep()結束指定休眠時間后,這個線程不一定立即執行,因為此時其他線程可能正在運行。
wait方法是Object類里的方法,當一個線程執行到wait()方法時,它就進入到一個和該對象相關的等待池中,同時釋放了鎖對象,等待期間可以調用里面的同步方法,其他線程可以訪問,等待時不擁有CPU的執行權,否則其他線程無法獲取執行權。當一個線程執行了wait方法后,必須調用notify或者notifyAll方法才能喚醒,而且是隨機喚醒,若是被其他線程搶到了CPU執行權,該線程會繼續進入等待狀態。由於鎖對象可以時任意對象,所以wait方法必須定義在Object類中,因為Obeject類是所有類的基類。
二.是否可以傳入參數
sleep()方法必須傳入參數,參數就是休眠時間,時間到了就會自動醒來。
wait()方法可以傳入參數也可以不傳入參數,傳入參數就是在參數結束的時間后開始等待,不穿如參數就是直接等待。
三.是否需要捕獲異常
sleep方法必須要捕獲異常,而wait方法不需要捕獲異常。sleep方法屬於Thread類中方法,表示讓一個線程進入睡眠狀態,等待一定的時間之后,自動醒來進入到可運行狀態,不會馬上進入運行狀態,因為線程調度機制恢復線程的運行也需要時間,一個線程對象調用了sleep方法之后,並不會釋放他所持有的所有對象鎖,所以也就不會影響其他進程對象的運行。但在sleep的過程中過程中有可能被其他對象調用它的interrupt(),產生InterruptedException異常,如果你的程序不捕獲這個異常,線程就會異常終止,進入TERMINATED狀態,如果你的程序捕獲了這個異常,那么程序就會繼續執行catch語句塊(可能還有finally語句塊)以及以后的代碼。
wait屬於Object的成員方法,一旦一個對象調用了wait方法,必須要采用notify()和notifyAll()方法喚醒該進程;如果線程擁有某個或某些對象的同步鎖,那么在調用了wait()后,這個線程就會釋放它持有的所有同步資源,而不限於這個被調用了wait()方法的對象。wait()方法也同樣會在wait的過程中有可能被其他對象調用interrupt()方法而產生。
四.作用范圍
wait、notify和notifyAll方法只能在同步方法或者同步代碼塊中使用,而sleep方法可以在任何地方使用。但是注意sleep是靜態方法,也就是說它只對當前對象有效。通過對象名.sleep()想讓該對象線程進入休眠是無效的,它只會讓當前線程進入休眠。
五.調用者的區別
首先為什么wait、notify和notifyAll方法要和synchronized關鍵字一起使用?
因為wait方法是使一個線程進入等待狀態,並且釋放其所持有的鎖對象,notify方法是通知等待該鎖對象的線程重新獲得鎖對象,然而如果沒有獲得鎖對象,wait方法和notify方法都是沒有意義的,因此必須先獲得鎖對象再對鎖對象進行進一步操作於是才要把wait方法和notify方法寫到同步方法和同步代碼塊中了。
由此可知,wait和notify、notifyAll方法是由確定的對象即鎖對象來調用的,鎖對象就像一個傳話的人,他對某個線程說停下來等待,然后對另一個線程說你可以執行了(實質上是被捕獲了),這一過程是線程通信。sleep方法是讓某個線程暫停運行一段時間,其控制范圍是由當前線程決定,運行的主動權是由當前線程來控制(擁有CPU的執行權)。
其實兩者的區別都是讓線程暫停運行一段時間,但本質的區別一個是線程的運行狀態控制,一個是線程間的通信。
函數名 | 作用 |
wait | 當一個線程執行到wait()方法時,它就進入到一個和該對象相關的等待池中,同時釋放了對象的鎖,使得其他線程可以訪問。用戶可以使用notify、notifyAll或者指定睡眠時間來喚醒當前等待池中的線程。 注意:wait()、notify()、notifyAll()必須放在synchronized block中,否則會拋出異常
wait()方法可以傳入參數也可以不傳入參數,傳入參數就是在參數結束的時間后開始等待,不穿如參數就是直接等待。 wait方法不需要捕獲異常 |
sleep | 該函數是Thread的靜態函數,作用是使調用線程進入睡眠狀態。因為sleep()是Thread類的Static方法,因此他不能改變對象的鎖。所以,當在一個Sychronized塊中調用sleep()方法,線程雖然休眠了,但是對象的鎖並沒有釋放,其他線程無法訪問這個對象(即使睡着也持有對象鎖)
sleep()方法必須傳入參數,參數就是休眠時間,時間到了就會自動醒來。 sleep方法必須要捕獲異常 |
join | 等待目標線程執行完成之后再繼續執行 |
yield | 線程禮讓。目標線程由運行狀態轉換為就緒狀態,也就是讓出執行權限,讓其他線程得以優先執行,但其他線程能否優先執行是未知數 |
package com.adao.thread.yeild; public class YieldThread extends Thread { public YieldThread(String name) { super(name); } public synchronized void run() { for (int i = 0; i < 5; i++) { System.out.println(String.format("%s [%d] ---> %d", this.getName(), this.getPriority(), i)); // 當i為2時調用當前線程的yield函數 // if (i == 2) { // Thread.yield(); // } } } public static void main(String[] args) { YieldThread t1 = new YieldThread("thread-1"); YieldThread t2 = new YieldThread("thread-2"); t1.start(); t2.start(); } }
結果:
取消屏蔽:
我們看到執行到的thread-2 等於2之前交替執行,等於2時,thread-2讓出執行時間,執行thread-1,thread-1執行到2時又將執行權讓出
JOIN
package com.adao.thread.yeild; public class Worker extends Thread { public Worker(String name) { super(name); } @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("work in " + getName()); } public static void main(String[] args) { Worker worker1 = new Worker("work-1"); Worker worker2 = new Worker("work-2"); worker1.start(); System.out.println("啟動線程1"); try { // 調用work1的join函數,主線程會阻塞直到work1執行完成 worker1.join(); System.out.println("啟動線程2"); // 再啟動線程2,並且調用線程2的join函數,主線程會阻塞直到worker2執行完成 worker2.start(); worker2.join(); } catch (Exception ignored) { } System.out.println("主線程繼續執行"); } }
Wait:
package com.adao.thread.yeild; public class Wait { private static Object sLockObject = new Object(); static void waitAndNotifyAll() { System.out.println("主線程運行"); Thread thread = new WaitThread(); thread.start(); long startTime = System.currentTimeMillis(); try { synchronized (sLockObject) { System.out.println("主線程等待"); sLockObject.wait(); } } catch (Exception ignored) { } long timsMs = (System.currentTimeMillis() - startTime); System.out.println("主線程繼續-->等待耗時:" + timsMs + "ms"); } static class WaitThread extends Thread { @Override public void run() { try { synchronized (sLockObject) { Thread.sleep(3000); sLockObject.notifyAll(); } } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { Wait.waitAndNotifyAll(); } }