實作Runnable接口,重寫run()方法(避免多繼承局限)
繼承Thread類,重寫run()方法(本質:Thread類也是實作Runnable接口)
實作Callable接口,重寫call()方法,有傳回值
使用線程池(使用原因:不推薦手動建立線程,不友善管理,易造成較大開銷或浪費)
在Java中,我們可以利用多線程來最大化地壓榨CPU多核計算的能力。但是,線程本身是把雙刃劍,我們需要知道它的利弊,才能在實際系統中遊刃有餘地運用。
線程池,本質上是一種對象池,用于管理線程資源。 在任務執行前,需要從線程池中拿出線程來執行。在任務執行完成之後,需要把線程放回線程池。通過線程的這種反複利用機制,可以有效地避免直接建立線程所帶來的壞處。
不使用線程池的壞處:
頻繁的線程建立和銷毀會占用更多的CPU和記憶體;
頻繁的線程建立和銷毀會對GC産生比較大的壓力;
線程太多,線程切換帶來的開銷将不可忽視;
線程太少,多核CPU得不到充分利用,是一種浪費。
線程池的好處:
降低資源的消耗。線程本身是一種資源,建立和銷毀線程會有CPU開銷;建立的線程也會占用一定的記憶體;
提高任務執行的響應速度。任務執行時,可以不必等到線程建立完之後再執行;
提高線程的可管理性。線程不能無限制地建立,需要進行統一的配置設定、調優和監控。
是以,我們有必要對線程池進行比較完整地說明,以便能對線程池進行正确地治理。

線程池主要處理流程
通過上圖,我們看到了線程池的主要處理流程。我們的關注點在于,任務送出之後是怎麼執行的。大緻如下:
判斷核心線程池是否已滿,如果不是,則建立線程執行任務;
如果核心線程池滿了,判斷隊列是否滿了,如果隊列沒滿,将任務放在隊列中;
如果隊列滿了,則判斷線程池是否已滿,如果沒滿,建立線程執行任務;
如果線程池也滿了,則按照拒絕政策對任務進行處理。
在jdk裡面,我們可以将處理流程描述得更清楚一點。來看看<code>ThreadPoolExecutor</code>的處理流程。
ThreadPoolExecutor的處理流程:
<code>corePool</code> -> 核心線程池
<code>maximumPool</code> -> 線程池
<code>BlockQueue</code> -> 隊列
<code>RejectedExecutionHandler</code> -> 拒絕政策
Executors類建立線程池的方法歸根結底都是調用ThreadPoolExecutor類,隻不過對每個方法指派不同的參數去構造ThreadPoolExecutor對象。
newCachedThreadPool:建立一個可緩存的線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則建立線程。
newFixedThreadPool:建立一個定長線程池,可控制線程最大并發數,超出的線程會在隊列中等待
newScheduledThreadPool:建立一個定長線程池,支援定時及周期性任務執行。
newSingleThreadExecutor:建立一個單線程化的線程池,它隻會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。
注意:線程池不允許使用Executors去建立,而是通過ThreadPoolExecutor的方式建立。
原因:上述四個方法的建立的隊列大小預設都是Integer.MAX_VALUE,堆積過多的任務請求會可能導緻OOM。
corePoolSize: 常駐核心線程數,如果大于0,即使本地任務執行完也不會被銷毀
maximumPoolSize: 線程池能夠容納可同時執行的最大線程數
keepAliveTime: 線程池中線程空閑的時間,當空閑時間達到該值時,線程會被銷毀, 隻剩下 corePoolSize 個線程數量。
unit: 空閑時間的機關。一般以TimeUnit類定義時分秒。
workQueue: 當請求的線程數大于 corePoolSize 時,線程進入該阻塞隊列。
LinkedBlockingQueue:無界隊列,當不指定隊列大小時,将會預設為Integer.MAX_VALUE大小的隊列,是以大量的任務将會堆積在隊列中,最終可能觸發OOM。
ArrayBlockingQueue:有界隊列,基于數組的先進先出隊列,此隊列建立時必須指定大小。
PriorityBlockingQueue:有界隊列,基于優先級任務的,它是通過Comparator決定的。
SynchronousQueue:這個隊列比較特殊,它不會儲存送出的任務,而是将直接建立一個線程來執行新來的任務
threadFactory: 線程工廠,用來生産一組相同任務的線程,同時也可以通過它增加字首名,虛拟機棧分析時更清晰
handler: 執行拒絕政策,當 workQueue 已滿,且超過maximumPoolSize 最大值,就要通過這個來處理,比如拒絕,丢棄等,這是一種限流的保護措施。
AbortPolicy:預設的拒絕政策,抛RejectedExecutionException異常
DiscardPolicy:相當大膽的政策,直接丢棄任務,沒有任何異常抛出
DiscardOldestPolicy:丢棄最老的任務,其實就是把最早進入工作隊列的任務丢棄,然後把新任務加入到工作隊列
CallerRunsPolicy:送出任務的線程自己去執行該任務
線程池關閉
shutdown() : 不會立刻終止線程,等所有緩存隊列中的任務都執行完畢後才會終止。
shutdownNow() : 立即終止線程池,并嘗試打斷正在執行的任務,并且清空任務緩存隊列,傳回尚未執行的任務
線程池監控
long getTaskCount():擷取已經執行或正在執行的任務數
long getCompletedTaskCount():擷取已經執行的任務數
int getLargestPoolSize():擷取線程池曾經建立過的最大線程數,根據這個參數,我們可以知道線程池是否滿過
int getPoolSize():擷取線程池線程數
int getActiveCount():擷取活躍線程數(正在執行任務的線程數)
注意需要引入guava包,否則ThreadFactoryBuilder會報錯