關於執行緒池的面試題
- 問題
- 1. 為什麼要使用執行緒池,執行緒池用什麼用
- 2. 說說幾種常見的執行緒池及使用場景
- 3. 執行緒池有哪幾種工作佇列
- 4. 怎麼理解無界佇列和有界佇列
- 5. 執行緒池中的幾種重要的引數及流程
- 6. 單機上一個執行緒正在處理服務,如果忽然斷電了怎麼辦(正在處理和阻塞佇列裡的請求怎麼處理)
- 參考
問題
問題:
- 單機上一個執行緒正在處理服務,如果忽然斷電了怎麼辦(正在處理和阻塞佇列裡的請求怎麼處理)
- 為什麼要使用執行緒池,執行緒池用什麼用
- 說說幾種常見的執行緒池及使用場景
- 執行緒池有哪幾種工作佇列
- 怎麼理解無界佇列和有界佇列
- 執行緒池中的幾種重要的引數及流程
1. 為什麼要使用執行緒池,執行緒池用什麼用
- 降低資源消耗:通過重用已經建立的執行緒來降低執行緒建立和銷燬的消耗
- 提高響應速度:任務到達時不需要等待執行緒建立就可以立即執行
- 提高執行緒的可管理性:執行緒池可以統一管理、分配、調優和監控
2. 說說幾種常見的執行緒池及使用場景
- newFixedThreadPool(固定大小的執行緒池)
- newSingleThreadExecutor(單執行緒執行緒池)
- newCachedThreadPool(可快取執行緒的執行緒池)
- newScheduledThreadPool
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
執行緒池特點:
- 核心執行緒數和最大執行緒數大小一樣
-
keepAliveTime
為0 - 阻塞佇列是
LinkedBlockingQueue
它是固定大小的執行緒池,其核心執行緒數和最大執行緒數大小一樣。並且阻塞佇列用的是LinkedBlockingQueue
,也就是說執行緒最大數這個引數失效了基本,所以不會出現外包執行緒的存在,所以也可以認為keepAliveTime
引數是一個擺設。除非allowCoreThreadTimeOut
方法的呼叫。
該執行緒池的工作機制是:
- 執行緒數少於核心執行緒數,也就是設定的執行緒數時,新建執行緒執行任務
- 執行緒數等於核心執行緒數後,將任務加入阻塞佇列
- 由於佇列容量非常大(
Integer.MAX_VALUE
),可以一直加加加。(當執行緒池中的任務比較特殊時,比如關於資料庫的長時間的IO操作,可能導致OOM)
- 由於佇列容量非常大(
- 執行完任務的執行緒反覆去佇列中取任務執行
適用場景:
FixedThreadPool
適用於處理CPU密集型的任務,確保CPU在長期被工作執行緒使用的情況下,儘可能的少的分配執行緒即可。一般Ncpu+1
newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
執行緒池特點:
- 核心執行緒數和最大執行緒數大小一樣且都是1
-
keepAliveTime
為0 - 阻塞佇列是
LinkedBlockingQueue
該執行緒池的工作機制是:
- 執行緒池中沒有執行緒時,新建一個執行緒執行任務
- 有一個執行緒以後,將任務加入阻塞佇列,不停加加加
- 唯一的這一個執行緒不停地去佇列裡取任務執行
適用場景:
SingleThreadExecutor
適用於序列執行任務的場景,每個任務必須按順序執行,不需要併發執行。
newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
執行緒池特點:
- 核心執行緒數為0,且最大執行緒數為
Integer.MAX_VALUE
- 阻塞佇列是
SynchronousQueue
SynchronousQueue:一個不儲存元素的阻塞佇列。每個插入操作必須等到另一個執行緒呼叫移除操作,否則插入操作一直處於阻塞狀態,吞吐量通常要高於LinkedBlockingQueue
鎖當提交任務的速度大於處理任務的速度時,每次提交一個任務,就必然會建立一個執行緒。極端情況下會建立過多的執行緒,耗盡 CPU 和記憶體資源。由於空閒 60 秒的執行緒會被終止,長時間保持空閒的 CachedThreadPool 不會佔用任何資源。
該執行緒池的工作機制是:
- 沒有核心執行緒,直接向
SynchronousQueue
中提交任務 - 如果有空閒執行緒,就去取出任務執行;如果沒有空閒執行緒,就新建一個
- 執行完任務的執行緒有60秒生存時間,如果在這個時間內可以接到新任務,就可以繼續活下去,否則就拜拜
適用場景:
CachedThreadPool
用於併發執行大量短期的小任務。
newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;
執行緒池特點:
- 最大執行緒數為
Integer.MAX_VALUE
- 阻塞佇列是
DelayedWorkQueue
ScheduledThreadPoolExecutor 新增任務提供了另外兩個方法:
- scheduleAtFixedRate() :按某種速率週期執行
- scheduleWithFixedDelay():在某個延遲後執行
兩種方法的內部實現都是建立了一個ScheduledFutureTask
物件封裝了任務的延遲執行時間及執行週期,並呼叫decorateTask()
方法轉成RunnableScheduledFuture
物件,然後新增到延遲佇列中。
DelayQueue:中封裝了一個優先順序佇列,這個佇列會對佇列中的ScheduledFutureTask
進行排序,兩個任務的執行 time 不同時,time 小的先執行;否則比較新增到佇列中的ScheduledFutureTask
的順序號 sequenceNumber
,先提交的先執行。
該執行緒池的工作機制是:
- 呼叫上面兩個方法新增一個任務
- 執行緒池中的執行緒從 DelayQueue 中取任務
- 然後執行任務
具體執行步驟:
- 執行緒從 DelayQueue 中獲取 time 大於等於當前時間的
ScheduledFutureTask
DelayQueue.take()
- 執行完後修改這個 task 的 time 為下次被執行的時間
- 然後再把這個 task 放回佇列中
DelayQueue.add()
適用場景:
ScheduledThreadPoolExecutor
用於需要多個後臺執行緒執行週期任務,同時需要限制執行緒數量的場景。
3. 執行緒池有哪幾種工作佇列
- ArrayBlockingQueue (有界佇列):是一個基於陣列結構的有界阻塞佇列,此佇列按 FIFO(先進先出)原則對元素進行排序。
- LinkedBlockingQueue (無界佇列):一個基於連結串列結構的阻塞佇列,此佇列按FIFO (先進先出) 排序元素,吞吐量通常要高於ArrayBlockingQueue。靜態工廠方法Executors.newFixedThreadPool()使用了這個佇列。
- SynchronousQueue(同步佇列): 一個不儲存元素的阻塞佇列。每個插入操作必須等到另一個執行緒呼叫移除操作,否則插入操作一直處於阻塞狀態,吞吐量通常要高於LinkedBlockingQueue,靜態工廠方法Executors.newCachedThreadPool使用了這個佇列。
- DelayQueue(延遲佇列):一個任務定時週期的延遲執行的佇列。根據指定的執行時間從小到大排序,否則根據插入到佇列的先後排序。
- PriorityBlockingQueue(優先順序佇列): 一個具有優先順序得無限阻塞佇列。
4. 怎麼理解無界佇列和有界佇列
- 有界佇列即長度有限,滿了以後ArrayBlockingQueue會插入阻塞。
- 無界佇列就是裡面能放無數的東西而不會因為佇列長度限制被阻塞,但是可能會出現OOM異常。
5. 執行緒池中的幾種重要的引數及流程
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
-
corePoolSize
:核心池的大小,在建立了執行緒池後,預設情況下,執行緒池中並沒有任何執行緒,而是等待有任務到來才建立執行緒去執行任務,當有任務來之後,就會建立一個執行緒去執行任務,當執行緒池中的執行緒數目達到corePoolSize後,就會把到達的任務放到快取佇列當中 -
maximumPoolSize
:執行緒池最大執行緒數最大執行緒數 -
keepAliveTime
:表示執行緒沒有任務執行時最多保持多久時間會終止 -
unit
:引數keepAliveTime的時間單位TimeUtil類的列舉類(DAYS、HOURS、MINUTES、SECONDS 等) -
workQueue
:阻塞佇列,用來儲存等待執行的任務 -
threadFactory
:執行緒工廠,主要用來建立執行緒 -
handler
:拒絕處理任務的策略-
AbortPolicy
:丟棄任務並丟擲 RejectedExecutionException 異常。(預設這種) -
DiscardPolicy
:也是丟棄任務,但是不丟擲異常 -
DiscardOldestPolicy
:丟棄佇列最前面的任務,然後重新嘗試執行任務(重複此過程) -
CallerRunsPolicy
:由呼叫執行緒處理該任務
-
執行流程
- 當有任務進入時,執行緒池建立執行緒去執行任務,直到核心執行緒數滿為止
- 核心執行緒數量滿了之後,任務就會進入一個緩衝的任務佇列中
- 當任務佇列為無界佇列時,任務就會一直放入緩衝的任務佇列中,不會和最大執行緒數量進行比較
- 當任務佇列為有界佇列時,任務先放入緩衝的任務佇列中,當任務佇列滿了之後,才會將任務放入執行緒池,此時會拿當前執行緒數與執行緒池允許的最大執行緒數進行比較,如果超出了,則預設會丟擲異常。如果沒超出,然後執行緒池才會建立執行緒並執行任務,當任務執行完,又會將緩衝佇列中的任務放入執行緒池中,然後重複此操作。
6. 單機上一個執行緒正在處理服務,如果忽然斷電了怎麼辦(正在處理和阻塞佇列裡的請求怎麼處理)
經過網上查閱,發現基本是沒有一個明確的回答的。不過思考過後一番,我感覺實現思路和MySQL的redo,undo功能很相似,我們可以對正在處理和阻塞佇列的任務做事物管理或者對阻塞佇列中的任務持久化處理,並且當斷電或者系統崩潰,操作無法繼續下去的時候,可以通過回溯日誌的方式來撤銷正在處理的
已經執行成功的操作。然後重新執行整個阻塞佇列。
即:
阻塞佇列持久化,正在處理事物控制。斷電之後正在處理的回滾,日誌恢復該次操作。伺服器重啟後阻塞佇列中的資料再載入
參考
相關文章
- 面試題-關於Java執行緒池一篇文章就夠了面試題Java執行緒
- Java面試題:執行緒池內“鬧情緒”的執行緒,怎麼辦?Java面試題執行緒
- 面試場景題:一次關於執行緒池使用場景的討論。面試執行緒
- 敲開阿里大門的執行緒、多執行緒和執行緒池面試專題阿里執行緒面試
- Java面試經典題:執行緒池專題Java面試執行緒
- 關於執行緒的問題...執行緒
- 執行緒池相關執行緒
- 執行緒池是怎麼回事(附面試題)執行緒面試題
- 面試-執行緒池的成長之路面試執行緒
- 聊聊面試中的 Java 執行緒池面試Java執行緒
- fastapi 關於執行緒池、同步、非同步的問題解答彙總ASTAPI執行緒非同步
- Java面試必問之執行緒池的建立使用、執行緒池的核心引數、執行緒池的底層工作原理Java面試執行緒
- 面試被問執行緒池,真香面試執行緒
- 淺談執行緒池(上):執行緒池的作用及CLR執行緒池執行緒
- java面試一日一題:java執行緒池Java面試執行緒
- 萬字長文詳解Java執行緒池面試題Java執行緒面試題
- 執行緒池關閉的小結執行緒
- 淺談執行緒池(中):獨立執行緒池的作用及IO執行緒池執行緒
- Java執行緒池二:執行緒池原理Java執行緒
- 執行緒的建立及執行緒池執行緒
- 執行緒池的設計問題執行緒
- 執行緒和執行緒池執行緒
- 多執行緒【執行緒池】執行緒
- 執行緒 執行緒池 Task執行緒
- 多執行緒-多執行緒常見的面試題執行緒面試題
- 執行緒池相關複習執行緒
- 關於執行緒池,那些你還不知道的事執行緒
- 美團面試題:Java-執行緒池 ThreadPool 專題詳解面試題Java執行緒thread
- 多執行緒面試題執行緒面試題
- 高併發面試:執行緒池的七大引數?手寫一個執行緒池?面試執行緒
- java--執行緒池--建立執行緒池的幾種方式與執行緒池操作詳解Java執行緒
- 優雅關閉執行緒池的方案執行緒
- 對於es執行緒池使用的思考執行緒
- 關於js執行緒問題的解讀JS執行緒
- 執行緒與執行緒池的那些事之執行緒池篇(萬字長文)執行緒
- 面試中關於多執行緒同步,你必須要思考的問題面試執行緒
- 執行緒池執行緒
- 上海某小公司面試題:Java執行緒池來聊聊面試題Java執行緒