多線程(二)——線程狀態和經常使用方法、守護線程

1、線程狀態  

     1-新建狀態  ( New ):java

         使用 new 關鍵字和 Thread 類或其子類創建一個線程對象後,該線程對象就處於新建狀態。它保持這個狀態直到程序 start() 這個線程。程序員

         2-就緒狀態 ( Runnable ):數據庫

         當線程對象調用了start()方法以後,該線程就進入就緒狀態。就緒狀態的線程處於就緒隊列中,要等待JVM裏線程調度器的調度。網絡

         3-運行狀態 ( Running ):多線程

         若是就緒狀態的線程獲取 CPU 資源,就能夠執行 run(),此時線程便處於運行狀態。處於運行狀態的線程最爲複雜,它能夠變爲阻塞狀態、就緒狀態和死亡狀態。併發

         4-阻塞狀態 ( Blocked ):ide

         若是一個線程執行了sleep(睡眠)、suspend(掛起)等方法,失去所佔用資源以後,該線程就從運行狀態進入阻塞狀態。在睡眠時間已到或得到設備資源後能夠從新進入就緒狀態。能夠分爲三種:性能

                   等待阻塞:運行狀態中的線程執行 wait() 方法,使線程進入到等待阻塞狀態。this

                   同步阻塞:線程在獲取 synchronized 同步鎖失敗(由於同步鎖被其餘線程佔用)。spa

                   其餘阻塞:經過調用線程的 sleep() 或 join() 發出了 I/O 請求時,線程就會進入到阻塞狀態。當sleep() 狀態超時,join() 等待線程終止或超時,或者 I/O 處理完畢,線程從新轉入就緒狀態。sleep() 不會釋放持有的同步鎖

         5-死亡狀態 (Dead):

         一個運行狀態的線程完成任務或者因異常退出了run()方法,該線程就切換到終止狀態。死亡狀態的線程不能夠再執行

線程狀態轉換圖:

          

2、線程調度 

  Java線程調度是Java多線程的核心,只有良好的調度,才能充分發揮系統的性能,提升程序的執行效率。可是無論程序員怎麼編寫調度,只能最大限度的影響線程執行的次序,而不能作到精準控制

  線程優先級:java中的線程是具備優先級的,優先級高的線程會得到更多的運行機會。

  Java中線程的優先級是用整數來表示的,取值範圍是1-10

  Thread類中有三個靜態常量:

  static int MAX_PRIORITY

  線程能夠具備的最高優先級,取值爲10。

  static int MIN_PRIORITY

  線程能夠具備的最低優先級,取值爲1。

  static int NORM_PRIORITY

       分配給線程的默認優先級,取值爲5。

  能夠用Thread類的setPriority和getPriority方法分別來設置和獲取線程的優先級

  線程的優先級有繼承關係,好比A線程中建立了B線程,那麼B將和A具備相同的優先級。

  注意:線程優先級不能保證線程執行的順序,只能說明優先級高的線程更加劇要

3、經常使用方法概括

  一、Thread.sleep(long millis) —— 線程睡眠

   sleep方法是Thread類提供靜態方法,表示始終讓當前正在執行的線程暫停一段時間,並進入阻塞狀態。使用時不要用實例對象調用,只會讓當前的線程進入睡眠,而不是使調用該方法的線程對象進入睡眠

   當睡眠的時間結束,線程從新進入到就緒狀態,而就緒狀態進入到運行狀態,是由系統控制的,咱們不可能精準的控制,因此若是調用Thread.sleep(1000)使得線程睡眠1秒,可能結果會大於1秒。

  二、Thread.yield() —— 線程讓步

  yield()方法也是Thread類提供靜態方法,它也可讓當前正在執行的線程暫停,讓出cpu資源給其餘的線程。可是和sleep()方法不一樣的是,它不會進入到阻塞狀態,而是進入到就緒狀態yield()方法只是讓當前線程暫停一下,從新進入就緒的線程池中,讓系統的線程調度器從新調度器從新調度一次,徹底可能出現這樣的狀況:當某個線程調用yield()方法以後,線程調度器也可能將其調度出來從新進入到運行狀態執行

  實際上,當某個線程調用了yield()方法暫停以後,優先級與當前線程相同,或者優先級比當前線程更高的就緒狀態的線程更有可能得到執行的機會,固然,只是有可能,由於咱們不可能精確的干涉cpu調度線程。用法以下:

class MyThread extends Thread { public MyThread(String name, int pro) { super(name);// 設置線程的名稱 this.setPriority(pro);// 設置優先級  } @Override public void run() { for (int i = 0; i < 30; i++) { System.out.println(this.getName() + "線程第" + i + "次執行!"); if (i % 5 == 0) Thread.yield(); } } } public class LifecycleThread { public static void main(String[] args) throws InterruptedException { new MyThread("低級", 1).start(); new MyThread("中級", 5).start(); new MyThread("高級", 10).start(); } }

關於sleep()方法和yield()方的區別以下:

①、sleep方法暫停當前線程後,會進入阻塞狀態,只有當睡眠時間到了,纔會轉入就緒狀態。而yield方法調用後 ,是直接進入就緒狀態,因此有可能剛進入就緒狀態,又被調度到運行狀態。

②、sleep方法聲明拋出了InterruptedException,因此調用sleep方法的時候要捕獲該異常,或者顯示聲明拋出該異常。而yield方法則沒有聲明拋出任務異常。

③、sleep方法比yield方法有更好的可移植性,一般不要依靠yield方法來控制併發線程的執行。

  sleep()使當前線程進入停滯狀態,因此執行sleep()的線程在指定的時間內確定不會被執行;yield()只是使當前線程從新回到可執行狀態,因此執行yield()的線程有可能在進入到可執行狀態後立刻又被執行。

        sleep 方法使當前運行中的線程睡眼一段時間,進入不可運行狀態,這段時間的長短是由程序設定的,yield 方法使當前線程讓出 CPU 佔有權,但讓出的時間是不可設定的。

  實際上,yield()方法對應了以下操做:先檢測當前是否有相同優先級的線程處於同可運行狀態,若有,則把 CPU  的佔有權交給此線程,不然,繼續運行原來的線程。因此yield()方法稱爲「退讓」,它把運行機會讓給了同等優先級的其餘線程

  另外,sleep 方法容許較低優先級的線程得到運行機會,但 yield()  方法執行時,當前線程仍處在可運行狀態,因此,不可能讓出較低優先級的線程些時得到 CPU 佔有權。在一個運行系統中,若是較高優先級的線程沒有調用 sleep 方法,又沒有受到 I\O 阻塞,那麼,較低優先級線程只能等待全部較高優先級的線程運行結束,纔有機會運行。 

  三、join —— 等待線程終止

  它不是靜態方法,應用場景是當一個線程必須等待另外一個線程執行完畢才能執行時,Thread類提供了join方法來完成這個功能。

  爲何要用join()方法?

 

  在不少狀況下,主線程生成並起動了子線程,若是子線程裏要進行大量的耗時的運算,主線程每每將於子線程以前結束,可是若是主線程處理完其餘的事務後,須要用到子線程的處理結果,也就是主線程須要等待子線程執行完成以後再結束,這個時候就要用到join()方法了。

  不加join代碼以下:

class MyThread extends Thread { public MyThread(String name, int pro) { super(name);// 設置線程的名稱 this.setPriority(pro);// 設置優先級  } @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(this.getName() + "" + i + "次執行!"); } } } public class LifecycleThread { public static void main(String[] args) throws InterruptedException { System.out.println("主線程開始"); MyThread t1 = new MyThread("子線程", 10); t1.start(); for (int i = 0; i < 10; i++) { System.out.println("主線程執行代碼"); } System.out.println("主線程結束"); } }
主線程開始 主線程執行代碼 主線程執行代碼 主線程執行代碼 主線程執行代碼 主線程執行代碼 主線程執行代碼 主線程執行代碼 主線程執行代碼 主線程執行代碼 主線程執行代碼 子線程第0次執行! 子線程第1次執行! 子線程第2次執行! 子線程第3次執行! 主線程結束 子線程第4次執行! 子線程第5次執行! 子線程第6次執行! 子線程第7次執行! 子線程第8次執行! 子線程第9次執行!
輸出結果

  這個輸出結果只是其中的一種狀況,主線程執行不會等待子線程。

  加 join 代碼以下:

class MyThread extends Thread { public MyThread(String name, int pro) { super(name);// 設置線程的名稱 this.setPriority(pro);// 設置優先級  } @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(this.getName() + "" + i + "次執行!"); } } } public class LifecycleThread { public static void main(String[] args) throws InterruptedException { System.out.println("主線程開始"); MyThread t1 = new MyThread("子線程", 10); t1.start();  t1.join(); for (int i = 0; i < 10; i++) { System.out.println("主線程執行代碼"); } System.out.println("主線程結束"); } }
主線程開始 子線程第0次執行! 子線程第1次執行! 子線程第2次執行! 子線程第3次執行! 子線程第4次執行! 子線程第5次執行! 子線程第6次執行! 子線程第7次執行! 子線程第8次執行! 子線程第9次執行! 主線程執行代碼 主線程執行代碼 主線程執行代碼 主線程執行代碼 主線程執行代碼 主線程執行代碼 主線程執行代碼 主線程執行代碼 主線程執行代碼 主線程執行代碼 主線程結束
輸出結果

  主線程必定會等子線程都結束了才結束。

  

四、interrupt()

  只是向線程發送一箇中斷信號,讓線程在無限等待時(如死鎖時)能拋出拋出,從而結束線程,可是若是你吃掉了這個異常,那麼這個線程仍是不會中斷的!

五、線程類的一些經常使用方法: 

  sleep(): 強迫一個線程睡眠N毫秒。 
  isAlive(): 判斷一個線程是否存活。 
  join(): 等待線程終止。 
  activeCount(): 程序中活躍的線程數。 
  enumerate(): 枚舉程序中的線程。 
    currentThread(): 獲得當前線程。 
  isDaemon(): 一個線程是否爲守護線程。 
  setDaemon(): 設置一個線程爲守護線程。(用戶線程和守護線程的區別在於,是否等待主線程依賴於主線程結束而結束) 
  setName(): 爲線程設置一個名稱。 
  wait(): 強迫一個線程等待。 
  notify(): 通知一個線程繼續運行。 
  setPriority(): 設置一個線程的優先級。

4、多線程使用的時機

  多線程的目的是讓程序併發執行,改變原有的串行執行方式,來提升效率,當程序中有兩個子系統須要併發執行的時候,就須要使用多線程

  多線程是能夠編寫出高效率的程序,可是必定要注意,太多的線程卻會讓程序的執行效率實際上下降,由於線程間的切換也是對CPU資源有開銷的,過多的線程會使CPU在上下文(線程之間)切換的時間大於程序執行的時間。

5、守護線程

Java線程分兩類:

  1.用戶線程:運行在前臺,執行具體任務(例如:主線程,鏈接網絡的子線程等)。
  2.守護線程:運行在後臺,爲其餘前臺線程服務,
  注意:一旦全部用戶線程結束運行,守護線程會隨JVM一塊兒結束工做;最多見守護線程:垃圾回收線程;數據庫鏈接池監測線程;JVM啓動的監測線程。

  Ps:如何設置守護線程:能夠經過調用Thread類的setDaemon(ture)方法來設置當前的線程爲守護線程。

相關文章
相關標籤/搜索