在上一章,爲你們介紹了線程的一些基礎知識,線程的建立與終止。本期將爲各位帶來線程的生命週期與經常使用方法。關注個人公衆號「Java面典」瞭解更多 Java 相關知識點。html
線程生命週期
一個線程不是被建立了立刻就開始執行,也不是一直處於執行狀態。在線程的整個生命週期中會經歷新建(New)、
就緒(Runnable)、運行(Running)、阻塞(Blocked)和銷燬(Terminated)5 種狀態。java
![線程全生命週期.PNG](http://static.javashuo.com/static/loading.gif)
新建
指使用 new 關鍵字建立一個新的線程對象後,該線程就處於新建狀態。此時僅由 JVM 爲其分配內存,並初始化其成員變量的值。多線程
就緒
當線程對象調用了 start() 方法以後,該線程處於就緒狀態。JVM 會爲其建立方法調用棧和程序計數器,等待調度運行。併發
運行
若是處於就緒狀態的線程得到了 CPU 時鐘,開始執行 run() 方法的線程執行體,則該線程處於運行狀態。操作系統
阻塞
阻塞狀態是指線程由於某種緣由放棄了 CPU 使用權,暫時中止運行。阻塞的狀況分三種:線程
- 等待阻塞(o.wait->等待對列):運行( running )的線程執行 o.wait( )方法,JVM 會把該線程放入等待隊列(waitting queue) 中;
- 同步阻塞(lock->鎖池):運行( running )的線程在獲取對象的同步鎖時,若該同步鎖被別的線程佔用,則 JVM 會把該線程放入鎖池( lock pool )中。
- 其餘阻塞(sleep/join): 運行( running )的線程執行 Thread.sleep(long ms)或 t.join()方法,或者發出了 I/O 請求時,JVM 會把該線程置爲阻塞狀態。當 sleep() 狀態超時、join() 等待線程終止或者超時、或者 I/O 處理完畢時,線程從新轉入可運行( runnable )狀態。
銷燬
若是線程正常執行完畢後或線程被提早強制性的終止或出現異常致使結束,那麼線程就要被銷燬,釋放資源。code
線程經常使用方法
從線程的生命週期中,咱們已經瞭解到了線程的一些經常使用方法。線程的經常使用方法有 wait,notify,notifyAll,sleep,join,yield等。htm
wait
- 做用:強迫一個線程等待,線程進入 WATING 狀態,只有等待另外線程的通知或被中斷纔會返回;
- 注意:調用 wait() 方法後,會釋放對象的鎖。所以,wait 方法通常用在同步方法或同步代碼塊中。
sleep
- 做用:強迫一個線程睡眠N毫秒,線程進入 TIMED_WATING 狀態, 只有等待另外線程的通知或被中斷纔會返回;
- 注意:與 wait 方法不一樣的是 sleep 不會釋放當前佔有的鎖。
yield
- 做用:使當前線程讓出 CPU 執行時間片,與其餘線程一塊兒從新競爭 CPU 時間片;
- 注意:通常狀況下,優先級高的線程有更大的可能性成功競爭獲得 CPU 時間片,但這又不是絕對的,有的操做系統對線程優先級並不敏感。
interrupt
- 做用:中斷一個線程,其本意是給這個線程一個通知信號,會影響這個線程內部的一箇中斷標識位;
- 注意:
- interrupt 不會改變線程的狀態,即不會強制線程進入阻塞、終止等狀態;
- 若線程處於 TIMED-WATING 狀態,這時調用 interrupt()方法,會拋出 InterruptedException,從而使線程提早結束 TIMED-WATING 狀態;
- 許多聲明拋出 InterruptedException 的方法(如 Thread.sleep(long mills 方法)),拋出異常前,都會清除中斷標識位,因此拋出異常後,調用 isInterrupted()方法將會返回 false;
- 能夠調用 thread.interrupt()方法,在線程的 run 方法內部能夠根據 thread.isInterrupted()的值來優雅的終止線程。
join
- 做用:使當前線程轉爲阻塞狀態,直到另外一個線程結束,當前線程再由阻塞狀態變爲就緒狀態;
- 使用場景:主線程中啓動子線程,主線程等待子線程的返回狀態。
System.out.println(Thread.currentThread().getName() + "線程運行開始!");
Thread6 thread1 = new Thread6();
thread1.setName("線程 B");
thread1.join();
System.out.println("這時 thread1 執行完畢以後才能執行主線程");
notify
- 做用:Object 類中的 notify() 方法,喚醒在此對象監視器上等待的單個線程;
若是全部線程都在此對象上等待,則會選擇喚醒其中一個線程,選擇是任意的,並在喚醒該線程前,調用該線程的 wait() 方法,在對象的監視器上等待,直到當前的線程放棄此對象上的鎖定,才能繼續執行被喚醒的線程,被喚醒的線程將以常規方式與在該對象上主動同步的其餘全部線程進行競爭。相似的方法還有 notifyAll() ,喚醒在此監視器上等待的全部線程。對象
其餘線程方法
- isAlive():判斷一個線程是否存活;
- activeCount():程序中活躍的線程數;
- enumerate():枚舉程序中的線程;
- currentThread():獲得當前線程;
- isDaemon():判斷一個線程是否爲守護線程;
- setDaemon():設置一個線程爲守護線程;
- setName():爲線程設置一個名稱;
- getPriority():獲取當前線程的優先級;
- setPriority():設置當前線程的優先級。
注意:線程優先級高,被CPU調度的機率大,但不表明必定會運行,還有小几率運行優先級低的線程。
什麼是守護線程
定義
守護線程——也稱「服務線程」,他是後臺線程,它有一個特性,即爲用戶線程提供公共服務,在沒有用戶線程可服務時會自動離開;blog
優先級
守護線程的優先級比較低,用於爲系統中的其它對象和線程提供服務;
設置
經過 setDaemon(true)來設置線程爲「守護線程」;將一個用戶線程設置爲守護線程的方式是在線程對象建立以前用線程對象的 setDaemon 方法;
特色
在 Daemon 線程中產生的新線程也是 Daemon 的;
example
垃圾回收線程就是一個經典的守護線程,當咱們的程序中再也不有任何運行的Thread,程序就不會再產生垃圾,垃圾回收器也就無事可作,因此當垃圾回收線程是 JVM 上僅剩的線程時,垃圾回收線程會自動離開。它始終在低級別的狀態中運行,用於實時監控和管理系統中的可回收資源;
生命週期
守護進程(Daemon)是運行在後臺的一種特殊進程。它獨立於控制終端而且週期性地執行某種任務或等待處理某些發生的事件。也就是說守護線程不依賴於終端,可是依賴於系統,與系統「同生共死」。當 JVM 中全部的線程都是守護線程的時候,JVM 就能夠退出了;若是還有一個或以上的非守護線程則 JVM 不會退出。
sleep() 與 wait() 的區別
- 所屬類不一樣:sleep() 方法屬於 Thread 類,wait() 方法屬於 Object 類;
- 鎖狀態不一樣:sleep() 方法不會釋放鎖,而 wait() 方法會釋放鎖;
- 再次運行方式不一樣:
- sleep() 方法致使了程序暫停執行指定的時間,讓出 cpu 該其餘線程,可是他的監控狀態依然保持者,當指定的時間到了又會自動恢復運行狀態;
- wait() 只有針對此對象調用 notify() 方法後,本線程才進入對象鎖定池準備獲取對象鎖進入運行狀態。
start() 與 run() 的區別
- 做用不用:start() 方法用來啓動線程,run() 是線程邏輯的具體方法體;
- 狀態不一樣:調用 start() 方法線程處於就緒狀態,並無運行,run() 執行時,線程處於運行狀態。
多線程與併發系列推薦
Java多線程併發01——線程的建立與終止,你會幾種方式