注意:調用obj.wait()的線程須要先獲取obj的monitor,wait()會釋放obj的monitor並進入等待態。因此wait()/notify()都要與synchronized聯用java
阻塞: 當一個線程試圖得到對象鎖(非juc庫中的鎖,即synchronized),而該鎖被其餘線程持有,則該線程進入阻塞狀態。它的特色是使用簡單,由jvm調度器來決定喚醒本身,而不是須要由另外一個線程來顯式喚醒本身,不響應中斷api
等待: 當一個線程等待另外一個線程通知調度器一個條件時,該線程進入等待狀態。它的特色是須要等待另外一個線程顯式地喚醒本身,實現靈活,語意更豐富,可響應中斷。例如: Object.wait(), Thread.join() 以及等待lock或condition安全
須要強調的是雖然synchronized和JUC裏的Lock都實現鎖的功能,但線程進入的狀態是不同的。synchronized會讓線程進入阻塞態,而JUC裏的lock是用LockSupport.park()/unpark()來實現阻塞/喚醒的,會讓線程進入等待態。但事實上,雖然等待鎖時進入的狀態不同,但被喚醒後又都進入runnable態,從行爲效果來看又是同樣的。多線程
sleep至關於讓線程睡眠,交出cpu,讓cpu去執行其餘的任務。可是sleep不會釋放鎖,也就是說若是當前線程持有對某個對象的鎖,則即便調用sleep方法,其餘線程也沒法訪問這個對象。框架
調用yield方法會讓當前線程交出cpu權限,讓cpu去執行其餘的線程。它跟sleep方法類型,一樣不會釋放鎖。可是jield不能控制具體的交出cpu的時間。jvm
注意調用yield方法並不會讓線程進入阻塞狀態,而是讓線程重回就緒狀態,它只須要等待從新得到Cpu執行時間。ide
join有三個重載版本函數
1 join() 2 join(long millis) //參數爲毫秒 3 join(long millis,int nanoseconds) //第一參數爲毫秒,第二個參數爲納秒
join()實際是利用了wait(),只不過它不用等待notify()/notifyAll(),且不受其影響。它結束的條件是:1)等待時間到;2)目標線程已經run完(經過isAlive()來判斷)。this
join和synchronized的區別是: join在內部使用wait()方法進行等待,而synchronized關鍵字使用的是"對象監視器"做爲同步。spa
public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } //0則須要一直等到目標線程run完 if (millis == 0) { while (isAlive()) { wait(0); } } else { //若是目標線程未run完且阻塞時間未到,那麼調用線程會一直等待。 while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } }
此操做會中斷等待中的線程,並將線程的中斷標誌位置位。若是線程在運行態則不會受此影響。
能夠經過如下三種方式來判斷中斷:
1)isInterrupted()
此方法只會讀取線程的中斷標誌位,並不會重置。
2)interrupted()
此方法讀取線程的中斷標誌位,並會重置。
3)throw InterruptException
拋出該異常的同時,會重置中斷標誌位。
掛起線程,直到被resume,纔會甦醒
但調用suspend()的線程和調用resume()的線程,可能會由於爭鎖的問題而發生死鎖,因此JDK 7開始已經不推薦使用了。
線程是操做系統可以進行運算調度的最小單位,它被包含在進程之中,是進程中實際運做單位。
線程是進程的子集,一個進程能夠有不少線程,每條線程並行執行不一樣的任務。不一樣的進程使用不一樣的內存空間,而全部的線程共享一片相同的內存空間。別把它和棧內存搞混,每一個線程都擁有單獨的棧內存用來存儲本地數據。
Java提供了很豐富的API但沒有爲中止線程提供API。JDK 1.0原本有一些像stop(), suspend() 和 resume()的控制方法。可是因爲潛在的死鎖威脅,在後續的JDK版本中他們被棄用了,以後Java API的設計者就沒有提供一個兼容且線程安全的方法來中止一個線程。當run() 或者 call() 方法執行完的時候線程會自動結束,若是要手動結束一個線程,你能夠用volatile 布爾變量來退出run()方法的循環或者是取消任務來中斷線程。
private class Runner extends Thread{ volatile boolean bExit = false; public void exit(boolean bExit){ this.bExit = bExit; } @Override public void run(){ while(!bExit){ System.out.println("Thread is running"); try { Thread.sleep(500); } catch (InterruptedException ex) { Logger.getLogger(ThreadTester.class.getName()).log(Level.SEVERE, null, ex); } } } }
Thread類的sleep和yield都是做用在當前正在執行的線程上運行,因此其餘處於等待狀態的線程上調用這些方法是沒有意義的。設置爲靜態代表在當前執行的線程上工做,避免開發錯誤地認爲能夠在其餘非運行線程調用這些方法。
最大的不一樣是: 在等待時wait會釋放鎖,而sleep一直持有鎖。wait一般用於線程間交互,sleep一般被用於暫停執行。
Java中線程的優先級分爲1-10這10個等級,若是小於1或大於10則JDK拋出IllegalArgumentException()的異常,默認優先級是5。在Java中線程的優先級具備繼承性,好比A線程啓動B線程,則B線程的優先級與A是同樣的。注意程序正確性不能依賴線程的優先級高低,由於操做系統能夠徹底不理會Java線程對於優先級的決定。
java api強制要求這麼作,不然會拋出IllegalMonitorStateException異常。還有一個緣由是爲了不wait和notify之間產生競態條件。
二者均可以向線程池提交任務,execute()方法的返回類型是void, 它定義在Executor接口中,而submit()方法能夠返回有計算結果獲得Future對象,它定義在ExecutorService接口中,它擴展了Executor接口。
二者都表明那些要在不一樣的線程中執行的任務。Runnable從jdk1.0就開始有了,Callable是在jdk1.5增長的。它們的主要區別是Callable的call()方法能夠返回值和拋出異常,而Runnable的run()沒有這些功能。Callable能夠裝載有計算結果的Future對象。
主要是由於java提供的鎖是對象級的而不是線程級的,每一個對象都有鎖,經過線程得到。wait,notify和notifyAll都是鎖級別的操做,因此把它們定義在Object類中由於鎖屬於對象。
Java中有兩種線程,一種是用戶線程,另外一種是守護線程。當進程中不存在非守護線程了,則守護線程自動銷燬。經過setDaemon(true)設置線程爲後臺線程。注意thread.setDaemon(true)必須在thread.start()以前設置,不然會報IllegalThreadStateException異常;在Daemon線程中產生的新線程也是Daemon的;在使用ExecutorService等多線程框架時,會把守護線程轉換爲用戶線程,而且也會把優先級設置爲Thread.NORM_PRIORITY。在構建Daemon線程時,不能依靠finally塊中的內容來確保執行關閉或清理資源的邏輯
首先要明確,只能在synchronized同步方法或者同步代碼塊中使用這些。在執行wait方法後,當前線程釋放鎖(這點與sleep, yield不一樣)。調用了wait函數的線程會一直等待,直到有其餘線程調用了同一個對象的notify或notifyAll方法。須要注意的是,被喚醒並不表明馬上得到對象的鎖,要等待執行notify方法的線程執行完,也即退出synchronized代碼塊後,當前線程纔會釋放鎖,進而wait狀態的線程才能夠得到該對象鎖
不在同步代碼塊會有IllegalMonitorStateException異常(RuntimeException)
* @throws IllegalMonitorStateException if the current thread is not
* the owner of the object's monitor.
notify方法只會(隨機)喚醒一個正在等待的線程,而notifyAll方法會喚醒全部正在等待的線程。若是一個對象以前沒有調用wait方法,那麼調用notify方法是沒有任何影響的
interrupted 判斷當時線程是否已是中斷狀態,執行後清除狀態標誌
isInterrupted 判斷當時線程是否已是中斷狀態,執行後不清除狀態標誌
public static boolean interrupted() { return currentThread().isInterrupted(true); } public boolean isInterrupted() { return isInterrupted(false); } private native boolean isInterrupted(boolean ClearInterrupted);