<code> 在實際的使用以及面試中時常碰到關於線程池的問題,但是線程池作為Java程序開發中的基礎組件 擁有相當重要的地位,因此結合源碼以及代碼實驗對線程池進行了探究,並用文字記錄下來。 /<code>
<code> 在實際的使用以及面試中時常碰到關於線程池的問題,但是線程池作為Java程序開發中的基礎組件 擁有相當重要的地位,因此結合源碼以及代碼實驗對線程池進行了探究,並用文字記錄下來。 /<code>
如有錯誤,不妥之處請各位大佬指出
- 線程池的核心參數
參數類型含義corePoolSizeint核心線程數maximumPoolSizeint最大並行線程數keepAliveTimelong非核心線程最大存活時間unitTimeUnit描述存活時間的單位workQueueBlockingQueue存放任務阻塞隊列threadFactoryThreadFactory線程工廠handlerRejectedExecutionHandler拒絕策略
corePoolSize: 線程池中的核心線程數,規定的是線程池中的常駐線程worker 的數量maximumPoolSize:線程池的線程最大並行數量,規定的是線程池允許的最大的並行線程數量,在核心線程worker 已滿 且 隊列已滿的情況下,會啟動非核心的 線程worker 來執行任務workQueue:核心執行線程已滿時用於存放任務的阻塞隊列,推薦使用帶有邊界的ArrayBlockingQueue
<code>線程池最基本的運行單元是線程池中的一個內部內,Worker : 1/<code>
<code>ThreadPoolExecutor extends AbstractExecutorService { private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); // java.lang.Integer@Native public static final int SIZE = 32; // 可見 Integer 是4個byte的長度, 使用了3位來標識線程池的狀態,其餘位數表示線程池 // 當前的worker的數量 private static final int COUNT_BITS = Integer.SIZE - 3; // 線程池允許的最大容量,注意 是理論上可以容納的最大的worker數量, // 應該沒有大佬會 private static final int CAPACITY = (1 << COUNT_BITS) - 1; // 線程池狀態標識 private static final int RUNNING = -1 << COUNT_BITS; private static final int SHUTDOWN = 0 << COUNT_BITS; private static final int STOP = 1 << COUNT_BITS; private static final int TIDYING = 2 << COUNT_BITS; private static final int TERMINATED = 3 << COUNT_BITS; //------------------------------------------------------------ // 獲取線程池當前狀態 private static int runStateOf(int c) { return c & ~CAPACITY; } // 獲取線程池當前 worker 數量 private static int workerCountOf(int c) { return c & CAPACITY; } // 通過位運算獲得 線程池狀態標識 + 線程池數量的 結果 private static int ctlOf(int rs, int wc) { return rs | wc; } /* 因為繼承了 AbstractQueuedSynchronizer,可知: worker 通過 cas的方式實現了線程安全 即 當我在接客的時候 不要往我的房間再塞人 */ Worker extends AbstractQueuedSynchronizer implements Runnable { // worker會從 ThreadFactory中獲得一個線程對象 public void run() { // 運行線程 runWorker(this); } // ... 其他方法是 創建線程對象,aqs方式對創建的線程進行運行,阻塞,中斷等行為的方法 } final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null; w.unlock(); // allow interrupts boolean completedAbruptly = true; try { while (task != null || (task = getTask()) != null) { w.lock(); try { // 線程池增強方法,加入線程池的前置操作,線程池監控常用 beforeExecute(wt, task); Throwable thrown = null; try { /** 調用 runable中的run(),執行業務代碼塊 **/ task.run(); } catch (RuntimeException x) { ... } finally { // 線程池增強方法,完成任務之後的後置操作,線程池監控常用 afterExecute(task, thrown); } } finally { task = null; w.completedTasks++; w.unlock(); } } completedAbruptly = false; } finally { // 記錄執行結果,並將Worker移除在執行Set集合 processWorkerExit(w, completedAbruptly); } } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475/<code>
- 線程池調度方式線程池的調度,我主要是指線程池當進入一個新任務時是如何完成整個生命週期的,新的任務進入 =>先查詢核心worker數是否已滿 ? 未滿則新增核心worker執行任務 ,
已滿則查看阻塞隊列,阻塞隊列未滿則加入阻塞隊列,
已滿 且 查看最大並行線程數是否已滿,
已滿則調用拒絕策略,拒絕接受新增任務
未滿則新增非核心worker處理該任務也就是說,在核心線程數以及隊列容量沒有用完之前,設置的最大線程數是無意義的,線程池不會以最大線程數來並行任務。同時,當我們設置阻塞隊列的時候 如果採用 阻塞隊列時如果 採用無邊界的隊列,或者不設置邊界,在極端情況下對應用是危險的,會因為 Task的堆積發生OOM,這也是阿里大佬在小本本上要求自己去實現ThreadLocalPool 而不使用Executors中提供的線程池的原因。 - 線程池中的拒絕策略實現自己的線程池拒絕策略就是這個接口,在線程池已滿的情況下,我們可以把任務放進MQ,Redis,應用內部隊列中保存起來,找特定的時間窗口再去執行,當然,具體情況具體分析,反正接口,他就在那裡
<code>public interface RejectedExecutionHandler { void rejectedExecution(Runnable r, ThreadPoolExecutor executor); } 123/<code>
JDK大佬提供的拒絕策略:AbortPolicy(默認):直接報錯DiscardPolicy:不報錯,悄悄的就丟了,注意是悄悄的,什麼反應都沒有DiscardOldestPolicy:不報錯,悄悄的把隊列中等得最久得丟了CallerRunsPolicy:調用者自己處理
- 使用線程池的注意事項
1,注意初始化線程池時確定合理的並行數2,不要使用沒有邊界的隊列3,拒絕策略需結合業務需求慎重選擇,當然如果項目規模不大,且併發量低請忽略,默認即可,因為根本走不到那一步,但是咱不能不知道4,碼字不易,如果您老吃得好,歡迎再來,如果要端回家,麻煩給打個標,註明本店位置,下次再見