線程池面試題
線程池面試題
1.線程池的核心參數(執行原理)
難3出現4
ThreadPoolExecutor的7個核心參數
-
- corePoolSize核心線程數量3
-
- maximumPoolSize最大線程數5:(核心線程數+臨時線程最大數2)
-
- keepAliveTime生存時間(臨時線程的存活時間)
-
- unit時間單位(臨時線程數的生存單位 秒或者毫秒)
-
- workQueue阻塞隊列:沒有空閑的核心線程進入阻塞隊列,隊慢進入臨時線程執行任務
-
- threadFactory線程工廠指定線程創建,設置名字是否是守護線程
-
- handle拒絕策略
執行原理:提交任務之后先看核心線程數是否滿了,如果沒有滿進入核心線程工作,如果核心線程滿了進入阻塞隊列,要是阻塞隊列也滿了就進入臨時線程工作,要是臨時線程和主線程工作結束就回去看阻塞隊列有沒有任務,但是如果臨時線程也滿了就會執行拒絕策略

四種解決策略:
-
- AbortPolicy直接拋出異常,默認的解決策略
-
- CallerRunsPolicy交給主線程執行任務
-
- DiscardOldesPolicy:刪除阻塞隊列中最靠前的任務
-
- DiscardPolicy:直接刪除任務,就是直接刪除新來的任務
2.線程中有哪些常見的阻塞隊列
難3出現3
- ArrayBlockingQueue:基于數組結構的有界阻塞隊列FIFO
- LinkedBlockingQueue:基于鏈表結構的有界阻塞隊列FIFO
- DelayWorkQueue:一線隊列,他可以保證每次出隊都是當前隊列中執行時間靠前的
- Synchronized:不存元素的阻塞隊列,每個插入都必須等待一個移除操作
LinkedBlockingQueue默認無界,支持有界(一般設置) 底層是鏈表 是懶惰的創建節點的時候添加數據 入隊會添加新的Node節點 頭尾兩把鎖 一遍出隊 一遍出隊 運用的多
ArrayBlockingQueue:必須有界 底層是數組 提前初始化Node數組 Node需要提前創建 一把鎖 必須給一個容量大小
3.如何確定核心線程數
難3出現3
IO密集型任務 一般文件讀寫 DB讀寫 網絡請求 大多數 核心線程數2N+1
cpu密集型任務 一般來說計算型代碼 Bitmap轉換 Gson轉換 核心線程數N+1
4.線程池種類
難3出現3
Executors中靜態方法便捷的創建線程池
- 固定線程數的線程池: 任務量知道就可以用 Executors.newFixedThreadPool(3)
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- 單線程化的線程池 Executors.newSingleThreadExecutor()
主線程和最大線程都是1 先提交的先執行 有循序的線程
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
- 可緩存的線程池 Executors.newCachedThreadPool()
核心線程數是0 都是臨時線程 任務數密集但是任務時間比較短的情況下
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- 延遲和周期執行的線程池Executors.newScheduledThreadPool(3)執行延遲任務的線程
public static ScheduledExecutorService
newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public class exegesis01 {
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
scheduledExecutorService.schedule(() -> {
System.out.println("hello world");
}, 5, java.util.concurrent.TimeUnit.SECONDS);
}//任務 延遲時間 單位
}
5.為什么不建議使用Executors創建線程池
允許最大隊列長度或者是最大線程數的最大值為int的最大值,會引起內存溢出推薦使用Threadpollexecutor
6.如何控制某個方法允許并發訪問線程數量
在多線程總提供了一個Semaphore信號量 創建一個Semaphore對象,可以給一個容量acquire()可以請求一個信號量,這時候信號量個數-1 release()釋放一個信號量,此時信號量個數加1
7.使用場景
你們的項目中哪些用到了線程池
- 批量導入:使用線程池+CountDownLatch批量把數據庫中發數據導入
- 數據匯總:調用多個接口來匯總數據,所有接口的沒有依賴關系相互獨立使用線程池+future來提升性能
- 異步線程:為了避免上級方法影響下級方法可以使用異步線程來調用下一個方法
8.談一談對ThreadLocal的理解
多線程對于解決線程安全的一個操作類,他會為每一個線程都分配一個獨立的線程副本,從而解決訪問沖突的問題。實現了線程內的資源共享
set() get() remove()
底層實現原理
ThreadLocal本質來說是一個線程內部存儲類,從而讓多個線程只操作自己內部的值,實現線程之間的數據隔離,ThreadLocalmap就是來操作存放數據的,調用set先獲取對象名字 然后判斷map是否存在 要是存在就數據存儲,不存在就新建。調用get方法還是先獲取線程名和線程對象,判斷不為空就調用getEmtry來獲取數組對象。remove和get類似
內存泄漏問題 ThreadLoca的key是弱引用,內存不夠就會回收,但是value是強應用,就不會回收就會有內存泄漏等等問題出現 如比使用remove來刪除
該文章為java黑馬程序員面試篇

浙公網安備 33010602011771號