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
// 原始寫法
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)使用工廠的好處:
- 記錄執行緒的建立資訊
- 統一管理某一類執行緒的建立