Java開發中線程是經常用到的技術,那麼讓我們來回顧一下一些線程中經常考慮的問題吧
1、線程分為用戶線程和守護線程,有什麼區別2、線程有哪些運行狀態
一、什麼是線程
線程,一個執行實體,正在執行的程序,擔當分配系統資源(CPU、內存)的實體。一個完整的線程包括,需要運行的邏輯和需要運行需要的資源。
二、線程的狀態
線程有哪些狀態呢,在Thread的代碼中的State枚舉已經很清楚了
public enum State {NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED;}
線程的狀態實際是7種,新建、就緒、運行、阻塞、等待、超時等待、終止。不過一般將就緒和運行都算作運行中
新建:新建的線程對象,還未調用start方法
就緒:已經具備了運行條件,等待CPU服務,這個狀態下,線程在就緒隊列中等待
運行:線程具備運行條件,並獲得了CPU資源
阻塞:線程被掛起,一般是加鎖獲取同步狀態被阻塞,放到阻塞隊列中,阻塞狀態消除後,會進入就緒狀態
等待:等待狀態表示當前線程需要等待其他線程做出一些特定動作(等待通知機制)
超時等待:在等待的基礎上,加上一定時間後返回就緒狀態
終止:線程已經運行完畢
那麼這些狀態是如何切換的呢,借用java並發編程藝術中的一張圖
start方法調用後進入運行狀態,wait、sleep等方法進入等待狀態或,yeild方法進入就緒狀態,lock和synchronized進入阻塞狀態
三、線程的分類
線程可以分為守護線程和用戶線程兩種,那麼什麼是用戶線程,什麼是守護線程呢?
1、用戶線程
用戶線程就是我們平時使用的用來處理邏輯的線程。
2、守護線程
守護線程,是服務線程,程序運行時在後臺提供的一種通用服務的線程。最常見的就是jvm的垃圾回收線程。
那麼守護線程相對普通的線程有什麼不同,是用來做什麼的呢?
1)守護線程主要為其他線程提供服務的2)在jvm中所有用戶線程停止運行後,只剩守護線程了,那麼jvm也會退出
了解了守護線程的特點了,那麼用戶線程和守護線程運行的實際效果呢?
首先看下Thread類型中的setDaemon方法,通過這個方法來設置是守護線程和用戶線程,默認是用戶線程
首先,先創建一個用戶線程,代碼如下
public static void main(String[] args){Thread task = new Thread(new Runnable(){@Overridepublic void run() {System.out.println("start thread ..." +System.currentTimeMillis());try {Thread.sleep(5000);} catch (InterruptedException e){e.printStackTrace();}System.out.println("end thread..."+System.currentTimeMillis());}});task.start();try {Thread.sleep(2000);} catch (InterruptedException e){e.printStackTrace();}System.out.println("end main..."+System.currentTimeMillis());}
運行結果
start thread ...1589291388584end main...1589291390585end thread...1589291393585
很明顯的看到在main函數結束以後,等待線程技術了,jvm才停止運行
那麼對代碼做一下修改,改為守護線程呢,代碼如下
public static void main(String[] args){Thread task = new Thread(new Runnable(){@Overridepublic void run(){System.out.println("start thread ..." +System.currentTimeMillis());try {Thread.sleep(5000);} catch (InterruptedException e){e.printStackTrace();}System.out.println("end thread..."+System.currentTimeMillis());}});task.setDaemon(true);task.start();try {Thread.sleep(2000);} catch (InterruptedException e){e.printStackTrace();}System.out.println("end main..."+System.currentTimeMillis());}
運行結果
start thread ...1589291497224end main...1589291499225
在main函數結束以後,jvm就退出來
守護線程的應用場景:
1、為用戶線程提供服務的線程
2、在任何情況下都能關閉的線程(文件的讀寫等不能直接關閉的操作最好不要使用守護線程)
守護線程的注意事項
1、守護線程狀態的設置必須在線程啟動之前
2、守護線程中創建的線程也是守護線程
那麼關於線程的知識點先說到這裡
上文中說到線程狀態的切換了,分享一個經常討論的問題sleep和wait的區別
常規的答案
1、sleep沒有釋放鎖,wait釋放了鎖
2、sleep是Thread的靜態方法,wait是鎖對象的方法
3、sleep是休眠一段時間,wait需要notify喚醒
是否感覺理解的太淺了,咱們一塊研究的深入一點
分別說一下sleep和wait從開始休眠到喚醒的狀態變化
1、sleep被調用後,釋放CPU資源,並在指定時間內不再去爭奪CPU資源,相當於告訴作業系統,在N時間內不需要管我了,這個過程中線程會進入等待隊列中。當休眠時間結束以後,線程進入就緒狀態,並進入就緒隊列,等待分配系統資源。
這裡經常會有一個誤區,就是認為在線程等到時間結束後,會直接進入運行狀態。線程只是進入就緒狀態。
現在了解了sleep調用過程中發生了什麼,那麼可以考慮下,在線程代碼中會經常發現有Thread.sleep(0)這種休眠0毫秒操作,這是為了什麼呢?
2、wait在調用的時候,首先釋放鎖狀態,然後線程進入到等待狀態,當其他線程調用notify時,會隨機喚醒一個等到線程,個人理解是把線程移入鎖對象的阻塞隊列當中,然後等到獲取鎖狀態,進入運行狀態。如果調用notifyAll方法會把所有的線程都喚醒,去搶佔鎖,在搶佔結束以後,沒有獲取到鎖的線程進入阻塞狀態。
關於Thread先說到這裡,如果有疑問可以留言。