1. 程式人生 > 其它 >Java併發程式設計基礎01-執行緒管理

Java併發程式設計基礎01-執行緒管理

執行緒建立、執行和設定

(1)Java中建立執行緒的主要兩種方式

  • 直接繼承Thread類,然後重寫run()方法。
  • 構建一個實現Runnable介面的類並重寫run()方法,然後建立該類的例項物件,並以其作為構造引數去建立Thread類的物件。建議首選這種方法,因為它可以帶來更多的擴充套件性。

(2)Thread類的主要屬性

  • ID:該屬性儲存了每個執行緒的唯一識別符號。
  • Name:該屬性儲存了執行緒的名字。
  • Priority:該屬性儲存了Thread物件的優先順序。執行緒優先順序的範圍為1~10,其中1表示最低優先順序,10表示最高優先順序。修改執行緒優先順序不能保證能發生任何事情,僅僅代表一種可能性
  • Status:該屬性儲存了執行緒的狀態。在Java中,執行緒有6種狀態——Thread.State列舉中定義這些狀態:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING和TERMINATED。
NEW:執行緒已經建立完畢但未開始執行。
RUNNABLE:執行緒正在JVM中執行。
BLOCKED:執行緒處於阻塞狀態,並且等待獲取監視器。
WAITING:執行緒在等待另一個執行緒。
TIMED_WAITING:執行緒等待另一個執行緒一定的時間。
TERMINATED:執行緒執行完畢。

(3)每個Java應用程式都至少有一個執行執行緒。在程式啟動時,JVM會自動建立執行執行緒執行程式的main()方法。

(4)一個Java程式將在所有非守護執行緒完成後結束。不會去等待守護執行緒,當所有非守護執行緒完成,程序結束!

(5)如果一個執行緒呼叫System.exit()命令去結束程式,那麼所有執行緒將會終止各自的運

行。

執行緒中斷

(1)使用task.interrupt()方法對執行緒進行中斷【當呼叫一個執行緒物件的interrupt()方法時,中斷狀態屬性將修改為true】,但是需要執行緒程式碼進行響應。

(2)利用了中斷標識狀態來進行自我結束的方法,繼承Thread,在程式中判斷isinterrupted()是否為true,來自行解決執行緒生命

(3)如果沒有繼承Thread,也可以在Runnable中使用Thread.interrupted()靜態方法來判斷

(4)在Thread類中,還有一個靜態方法interrupted(),也能用來檢測當前執行緒是否已被中斷。isInterrupted()方法不會修改執行緒的是否中斷屬性,而interrupted()方法會將中斷屬性設定為false。

(5)Java提供了InterruptedException異常,可以在檢測到執行緒中斷後丟擲該異常,並在run()方法中捕獲它。【使用異常方式的優勢就是指,可以捕獲異常,知曉執行緒是被中斷的

(6)當執行緒結束時,為什麼isinterrupted會為false?應該是執行緒自己結束時會將中斷狀態復原為false

執行緒休眠和喚醒

(1)執行緒休眠的經典方法:Thread類的sleep()方法。該方法接收一個long型別的引數——該引數是執行緒將要暫停的時長。【靜態方法,Thread.sleep()毫秒為單位】

注:當呼叫sleep()方法時,執行緒釋放CPU資源,停止執行指定的時間。在這段時間裡,執行緒並不消耗CPU時間,因此CPU可以執行其他任務。

(2)自定義休眠時間單位:可以使用TimeUnit列舉元素的sleep()方法。該方法呼叫當前Thread類的sleep()方法,使當前執行緒進入休眠。

(3)當執行緒在休眠中發生中斷時,該方法會立即丟擲一個InterruptedException異常,而不會等到休眠時間結束。

(4)yield方法也可以做到是執行緒釋放CPU資源,該方法告知JVM當前執行緒可以為其他任務放棄CPU資源。JVM並不保證一定會響應該請求

在主執行緒中等待執行緒執行完成

(1)當呼叫一個執行緒物件的join()方法時,發起呼叫的執行緒將會暫停,直到執行緒物件執行結束。注:

  • join()這樣做會阻塞主執行緒,但是好處也是有的,例如初始化任務作為單獨執行緒,則必須等待其結束
  • 先要開始執行緒,才能用join等待

(2)join方法會丟擲InterruptedException異常,但是觸發條件並不是對其進行中斷時,線上程join期間,對其進行中斷,並不會丟擲中斷異常!

守護執行緒的建立和執行

(1)設定守護執行緒的方法:setDaemon()方法只能在start()方法之前呼叫,一旦執行緒開始執行,其daemon狀態便不可修改。

(2)通過isDaemon()方法可以檢查執行緒是一個守護執行緒(此時方法返回true)還是一個非守護執行緒(此時方法返回為false)。

(3)守護執行緒通常是一個無限迴圈程式,最典型的應用是:JVM垃圾回收器;注意,JVM並不會等待守護執行緒執行完成,當JVM發現程式只有守護執行緒存在時,會將其殺死,並結束程式。

處理執行緒中的非受查異常

(1)Java提供了用於處理執行緒物件中丟擲的非檢查異常機制,該機制不是類似於try-catch機制,而是一種處理機制。一旦發生非受查異常,會立刻結束執行緒,有異常處理器就會轉入異常處理器執行。

(2)要使用該機制,必須實現一個處理非檢查異常的類。該類必須實現UncaughtExceptionHandler介面,並實現介面中宣告的uncaughtException()方法。使用thread.setUncaughtExceptionHandler(new ExceptionHandler())方法來設定處理器。

(3)如果執行緒物件沒有配置非受查異常處理器,則JVM會在控制檯中打印出異常資訊棧,然後結束異常丟擲執行緒的執行。【而配置了非受查異常處理器,也非常雞肋,只是可以對異常進行處理而已,執行緒還是會在異常處結束執行】

(4)Thread類中還定義了另一個用於處理非受查異常的方法,即靜態方法setDefault-

UncaughtExceptionHandler()。該方法可以為應用中所有執行緒物件設定預設的非受查異常處理器。

(5)JVM為非受查異常尋找處理器的順序

1、執行緒物件的非受查異常處理器。

2、執行緒組的非受查異常處理器。

3、預設的異常處理器

使用執行緒本地變數

(1)ThreadLocal類,實現該類的一個例項,並複寫initalValue方法

// 原始寫法
ThreadLocal<Date> a = new ThreadLocal<Date>() {
    @Override
    protected Date initialValue() {
        return new Date();
    }
};

// 簡單寫法:
ThreadLocal<Date> a = ThreadLocal.withInitial(() -> new Date());

注:第一次訪問執行緒本地變數時,若與該執行緒物件關聯的屬性值不存在,則將會觸發
initialValue()方法,它會為該屬性賦值並返回初始值。

(2)InheritableThreadLocal類提供了執行緒本地變數的繼承機制

執行緒分組

(1)一個ThreadGroup物件可以由一組執行緒物件或者其他ThreadGroup物件組成,形成一個執行緒的樹形結構。

注:

  • ThreadGroup類不是使用組合模式提供組織
  • 如果組1裡有其他執行緒組,則組1會自動遮蔽所有的執行緒物件,成為執行緒組的組。
  • 執行緒組的優先順序高於執行緒物件

(2)獲取處理器核心數的方法:

使用Runtime類的availableProcessors()方法[使用Runtime類的靜態方法getRuntime()得到當前應用的Runtime物件],可以得到JVM中可用的處理器數。

(3)建立執行緒組的正規化:

建立一個名為MyThreadGroup的類,並繼承ThreadGroup類進行擴充套件。ThreadGroup類沒有無參構造器,因此必須宣告一個擁有一個引數的構造器。為了處理執行緒組丟擲的異常,還需要重寫uncaughtException()方法

public class MyThreadGroup extends ThreadGroup {
   /**
    * Constructor of the class. Calls the parent class constructor
    * 
    * @param name
    */
   public MyThreadGroup(String name) {
      super(name);
   }
   /**
    * Method for process the uncaught exceptions
    */
   @Override
   public void uncaughtException(Thread t, Throwable e) {
      // Prints the name of the Thread
      System.out.printf("The thread %s has thrown an Exception\n", t.getId());
      // Print the stack trace of the exception
      e.printStackTrace(System.out);
      // Interrupt the rest of the threads of the thread group
      System.out.printf("Terminating the rest of the Threads\n");
      interrupt();
   }
}

(4)執行緒關聯執行緒組:

Thread t = new Thread(threadGroup, task);

(5)將threadgroup中的執行緒轉化為執行緒陣列的方法

Thread[] threads = new Thread[threadGroup.activeCount()];
threadGroup.enumerate(threads);

使用工程建立執行緒

(1)執行緒工廠的核心程式碼:

ThreadFactory介面只有一個名為newThread()的方法。該方法接收一個Runnable物件作為引數,並返回一個Thread物件。實現一個ThreadFactory介面時,必須覆蓋newThread()方法。

(2)使用工廠的好處:

  • 記錄執行緒的建立資訊
  • 統一管理某一類執行緒的建立