国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

Java線程池

2019-11-06 08:08:14
字體:
來源:轉載
供稿:網友

使用線程池的好處

1. 降低資源消耗。通過重復利用已創建的線程降低創建線程和銷毀造成的消耗 2. 提高響應速度。當任務到達時,任務可以不需要等到線程創建就能立即執行。 3. 提高線程的可管理性。

線程池的處理流程

這里寫圖片描述

線程池判斷核心線程池是否都在執行任務。如果不是,則創建一個新的線程來執行任務。如果核心線程池里的線程都在執行任務,那么則進入下一個流程線程池判斷工作隊列是否已經滿。如果工作隊列不滿,則把新提交的任務放到隊列中存儲起來。如果隊列滿了,則進入下一個流程。線程池判斷線程池的線程是否都處于工作狀態。如果沒有,則創建一個新的線程來執行任務。如果已經滿了,則交給飽和策略來處理這個任務。

ThreadPoolExcutore執行excutor方法的示意圖

這里寫圖片描述

ThreadPoolExecutor執行executor方法分4中情況:

1. 當前運行的線程少于corePoolSize,則創新線程來執行任務,這一步驟需要獲取全局鎖 2. 如果運行的線程大于或等于corePoolSize,則將任務加入到BlockingQueue。 3. 如果無法將任務加入BlockingQueue,則創建新的線程來處理任務,這一步驟需要獲取全局鎖 4. 如果創建新線程將使當前運行的線程超出maximumPoolSize,任務將被拒絕,并調用RejectExecutionHandler.rejectExecution()方法

詳細的拒絕規則如下:

AbortPolicy 為java線程池默認的阻塞策略,不執行此任務,而且直接拋出一個運行時異常,切記ThreadPoolExecutor.execute需要try catch,否則程序會直接退出。 DiscardPolicy 直接拋棄,任務不執行,空方法 DiscardOldestPolicy 從隊列里面拋棄head的一個任務,并再次execute 此task。 CallerRunsPolicy 在調用execute的線程里面執行此command,會阻塞入口

ThreadPoolExecutor采取上述步驟的總體設計思想,是為了在執行executor方法時,盡可能地避免獲取全局鎖。在ThreadPoolSize完成預熱之后(當前運行的線程數大于或者等于corePoolSize),幾乎所有的executor方法調用都是執行步驟2,而步驟2不需要獲取全局鎖

源碼實現

構造方法如下:

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; }

構造函數的參數詳解如下:

int corePoolSize:線程池的基本大小,當提交一個任務到線程池時,線程池會創建一個線程來執行任務,即使其他空閑的基本線程能夠執行新任務也會創建線程,等到需要執行的任務數大于corePoolSize大小時,就不會再創建。如果調用了線程池的PRestartAllCoreThreads()方法,線程池會提前創建并啟動所有基本線程。

int maximumPoolSize:線程池最大數量。如果隊列滿了,并且已創建的線程數小于最大線程數,則線程會再創建新的線程執行任務。需要注意的是,如果使用了無界隊列,那么這個參數就沒有作用了

long keepAliveTime:線程活動保持時間。線程池的工作線程空閑后,保持存活的時間。所以如果任務很多,并且每個任務執行的時間比較短,可以調大時間,提高線程的利用率。 TimeUnit unit:線程活動保持時間的單位,可選的單位有天(DAYS)、小時(HOURS)、分鐘(MINUTES)、秒(SECONDS)、毫秒(MILLISECODENS)、微秒(MICROSECONDS)、納秒(NANOSECONDS)

BlockingQueue<Runnable> workQueue :任務隊列,用于保存等待執行的任務的阻塞隊列。可選擇的阻塞隊列如下:

ArrayBlockingQueue:是一個基于數組結構的有界阻塞隊列,按FIFO排序。

LinkedBlockingQueue:一個基于鏈表結構的阻塞隊列,按FIFO排列,吞吐量高于ArrayBlockingQueue。靜態工廠方法Executors.newFixedThreadPool()使用了這個隊列

SynchronousQueue:一個不存儲元素的阻塞隊列。每個插入操作必須等到另一線程調用移除操作,否則插入操作一直處于阻塞狀態。吞吐量通常高于LinkedBlockingQueue,靜態工廠方法Executors.newCachedThreadPool使用了這個隊列。

ThreadFactory threadFactory:用于設置創建線程的工廠,可以通過線程工廠給每個創建出來的線程設置名字。使用開源框架guava提供的ThreadFactoryBuilder可以快速給線程池里的線程設置名字。

RejectedExecutionHandler handler:飽和策略。當隊列和線程池都滿了,說明線程池處于飽和狀態,那么必須采取一種策略出來提交的新任務。默認的AbortPolicy,表示無法處理新任務時拋出異常。也可以根據應用場景實現RejectedExecutionHandler接口自定義策略。

向線程池提交任務

可以使用兩個方法向線程池提交任務:execute方法和submit方法。executre方法由于提交不需要返回值的任務,所以無法判斷任務是否被線程池執行成功。submit方法用于提交需要返回值的任務。線程池會返回一個future類型的對象,通過future對象可以判斷任務是否執行成功,并且可以通過future的get()方法來獲取返回的值,get()方法會阻塞當前線程直到任務完成,而使用get(long timeout,TimeUnit unit)方法則會阻塞當前線程一段時間后立即返回,這個時候有可能任務還未執行完。

向線程池提交任務的executor()方法

public void execute(Runnable command) { if (command == null) throw new NullPointerException(); // 獲取線程池控制狀態 int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { // worker數量小于corePoolSize if (addWorker(command, true)) // 添加worker // 成功則返回 return; // 不成功則再次獲取線程池控制狀態 c = ctl.get(); } if (isRunning(c) && workQueue.offer(command)) { // 線程池處于RUNNING狀態,將命令(用戶自定義的Runnable對象)添加進workQueue隊列 // 再次檢查,獲取線程池控制狀態 int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) // 線程池不處于RUNNING狀態,將命令從workQueue隊列中移除 // 拒絕執行命令 reject(command); else if (workerCountOf(recheck) == 0) // worker數量等于0 // 添加worker addWorker(null, false); } else if (!addWorker(command, false)) // 添加worker失敗 // 拒絕執行命令 reject(command); }

3個步驟:

1. 如果運行的線程小于corePoolSize,則嘗試使用用戶定義的Runnalbe對象創建一個新的線程調用addWorker函數會原子性的檢查runState和workCount,通過返回false來防止在不應 該添加線程時添加了線程.

2. 如果一個任務能夠成功入隊列,在添加一個線城時仍需要進行雙重檢查(因為在前一次檢查后 該線程死亡了),或者當進入到此方法時,線程池已經shutdown了,所以需要再次檢查狀態, 若有必要,當停止時還需要回滾入隊列操作,或者當線程池沒有線程時需要創建一個新線程

3. 如果無法入隊列,那么需要增加一個新線程,如果此操作失敗,那么就意味著線程池已經shutdown或者已經飽和了,所以拒絕任務

線程池中的線程執行任務分兩種情況: (1) 在executor()方法中創建一個線程時,會讓這個線程執行當前任務 (2) 在線程池中創建了一個線程,然后這個線程反復從BlockingQueue獲取任務執行

關閉線程池

可以通過調用線程池的shutdown或shutdownNow方法來關閉線程池,原理如下:

遍歷線程池中的工作線程,然后逐個調用線程的interrupt方法來中斷線程,所以無法響應中斷的任務可能永遠無法終止。

shutdownNow與shutdown的區別:

shutdowNow首先將線程池的狀態設置成STOP,然后嘗試停止所有的正在執行或者暫停任務的線程,并返回等待執行任務的列表,shutdown只是將線程池的狀態設置成SHUTDOWN狀態,然后中斷所有沒有正在執行任務的線程。

通常調用shutdown方法來關閉線程,如果任務不一定要執行完,可以調用shutdowNow方法。

合理配置線程池

CPU密集型:應配置盡可能小的線程,如N+1(N為cpu個數)

IO密集型:應配置盡可能多的線程,如2*N(N為cpu個數)

混合型:如果可以拆分,可以拆分成CPU密集型和IO密集型

優先級不同的任務:可以使用PriorityBlockingQueue隊列

可以通過Runtime.getRuntime().availableProcessors()獲取CPU個數


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 买车| 恩施市| 五莲县| 峨眉山市| 邯郸县| 望城县| 辽中县| 石景山区| 色达县| 德惠市| 沙田区| 明水县| 东阿县| 胶州市| 临泉县| 深州市| 呈贡县| 沙洋县| 龙胜| 翁源县| 沂源县| 蒙山县| 密山市| 剑阁县| 吐鲁番市| 怀宁县| 黑水县| 黔东| 汾西县| 平遥县| 金秀| 澄迈县| 盱眙县| 岑溪市| 乌拉特前旗| 大港区| 贵德县| 鱼台县| 加查县| 嘉义县| 安义县|