關於JAVA線程池關鍵參數,拒絕策略,調度方式

<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,碼字不易,如果您老吃得好,歡迎再來,如果要端回家,麻煩給打個標,註明本店位置,下次再見


分享到:


相關文章: